회고록 블로그

[공부 필기] Java 기본 공부하기 (7) 본문

2. 프로그래밍 언어 공부/Java

[공부 필기] Java 기본 공부하기 (7)

김간장 2021. 9. 17. 15:43

역시나 프로그래밍 언어 문법에 대해서 공부하는건 너무 재미없다.

 

하지만 A, B, C도 모르고 작문을 할 수는 없듯이, 적어도 기본은 알아야 무엇이든 프로그램을 만들 수 있는 법이다.

오늘도 노잼을 이겨내며 강의를 듣자..


공부 중인 강의 : 윤성우 선생님, 윤성우의 열혈 Java 프로그래밍 강의, https://cafe.naver.com/cstudyjava

 

윤성우의 프로그래밍 스터디그룹 [C/... : 네이버 카페

윤성우의 스터디 공간입니다. C와 JAVA를 공부하시는 분들은 모두 들어오세요. ^^

cafe.naver.com


1. 패키지(Package)

   - 패키지 선언이 왜 필요할까

     ㄱ. 개발자는 모든 클래스를 다 정의할 수가 없음

           (가령, String 클래스도 개발자가 정의한게 아니라 기본적으로 제공하는 클래스인 것 처럼)

     ㄴ. 사실 직접 정의하는 클래스보다 가져다쓰는 클래스가 더 많을 수도 있음

     ㄷ. 클래스는 기본적으로 제공하는 클래스도 있지만, 기업에서 만든 클래스도 있음

           그런 클래스를 가져와서 활용하는 경우가 정말 많음

     ㄹ. 문제는 A사, B사, C사 등 많은 기업에서 만든 클래스의 '이름'이 충돌되지 않을수가 없음

           (e.g. A사에서 Add 라는 클래스를 만들었는데, B사에도 Add 라는 클래스가 있을 수 있음)

     ㅁ. 이때 클래스의 이름이 중복되기 때문에 문제가 몇가지 생김

            → 같은 공간(디렉터리)에 둘 수가 없음

            → 클래스를 사용하려 할 때, JVM은 A사에서 만든 클래스인지 B사에서 만든 클래스인지 구분을 하지못함

     ㅂ. 결국 이를 모두 해결하기 위해서는 클래스를 구분해주는 것이 필요함

           → 경로를 다르게 해서 어떤 위치에 있는 어떤 클래스에 접근하겠다고 명시적으로 작성해줘야 함

   - 즉, 패키지 선언은 클래스들의 경로를 달리하고

      그 경로를 코드에 포함해서 작성해야만 클래스에 접근이 가능하도록 한 것임

   - 사실 패키지 선언은 클래스들을 묶는다는 의미가 더 강함 (여러 비슷한 물건을 패킹하듯이)

   - 예시

      → com 디렉터리 안의 example1 디렉터리 안의 project1에 있는 클래스

package com.example1.project1;

public class Add {
	......
}

      → com 디렉터리 안의 example2 디렉터리 안의 aaa에 있는 클래스

package com.example2.aaa;

public class Add {
	......
}

   - 패키지 이름을 정할 땐, 관례가 있음 (패키지 이름의 중복을 방지하기 위함)

     → 패키지명은 "인터넷 도메인 이름의 역순"으로 구성해야만 함

     → 패키지 이름 끝에는 "클래스 정의한 주체나 팀의 이름"을 넣어야만 함

          (같은 회사 안에서도 패키지 이름을 중복해서 제작할 가능성을 줄이기 위함)

   - 패키지 선언 예시

public static void main(String[] args) {
	com.example1.project1.Add a1 = new com.example1.project1.Add();
	com.example2.aaa.Add a1 = new com.example2.aaa.Add();
}

   - 유의할 점

      → JVM은 classpath를 기준으로 'com'디렉터리를 찾고, 그 안에서 'example1' 디렉터리를 찾고, 또 그 안에서 'project1'을 찾음

      → 따라서, 'com' 디렉터리가 포함되어 있는 디렉터리를 classpath로 설정해야함

      → 즉, 아래와 같은 경우라면 com 디렉터리가 포함된 'D:\Coding Folder'가 classpath가 되어야 함

   - 명령 프롬프트에서 javac의 -d 옵션을 사용하면 패키지 경로에 맞춰 자동으로 디렉터리를 만들어줌

     → 개발자가 직접 디렉터리를 만들고 그곳에 클래스파일을 두어도됨

 

 

※ JVM 기본 라이브러리에 있는 패키지들의 이름을 보니 전부 '소문자'로만 작성하는 것 같음

대문자는 패키지 이름으로 잘 쓰지 않는 듯함

 

2. import

   - 코드 안에서 패키지 이름을 명시하면서 작성하면 너무 번거로움(아래 참고)

public static void main(String[] args) {
	com.example1.project1.Add a1 = new com.example1.project1.Add();
    ...
}

   - 하지만 import 키워드를 이용하면 패키지 이름을 항상 언급할 필요가 없음

import com.example1.project1.Add;

public static void main(String[] args) {
	Add a1 = new Add();
    ...
}

   - package 키워드가 쓰이는 위치 주의할 것

     → 패키지 선언은 패키지로 묶을 소스파일 안에서 사용해야함

           main 메소드 등 패키지를 가져다 쓸 위치에서 작성하는 것이 아님

   - import 키워드의 위치는 인스턴스 생성 등 내가 패키지를 가져다 쓸 곳에서 선언하는 것임

com.example.project1 패키지 안의 Circle.java에서 package 키워드가 쓰임
Circle 인스턴스를 생성하고 싶은 곳에서 import 키워드를 사용함

   - import로 클래스 하나를 선언했다면, 동일한 클래스명의 다른 패키지에 있는 클래스는 import 선언이 불가능함

     → 가급적 import 키워드는 제한적으로 사용해야한다고 말하지만, 실상 현업에서 많이 사용된다고 함

          제한적으로(?) 적절하게 사용하라고 조언해주심(?)

   - 아래와 같이 별(*)을 이용해서 사용할 수도 있음

     → 하지만 가급적 사용을 지양하고 있고, 실제 현업에서도 잘 사용하지 않는다고 함

          (한 패키지 안에 있는 수십/수백개의 클래스를 모두 import 해버리면, 나중에 클래스 이름 충돌이 발생할 가능성이 높기 때문)

import com.example1.project1.*;

 

3. 정보은닉

   -  어떤 클래스가 있다고 할 때, 다른 클래스에서는 이 클래스에 접근하지 못하도록(보이지 않도록),

      즉 해당 클래스 내부에서만 접근할 수 있도록 하는 것이 정보은닉.

   - 클래스를 정의할 때는 데이터(변수 등)와 기능(메소드 등)이 있음

   - 정리하자면 정보은닉은

     클래스 외부에는 데이터를 보이지 않게 가리고(클래스 외부에서 접근하지 못하게) + 해당 클래스 안에서만 사용할 수 있게 하고

     클래스 외부에서 데이터에 접근해야한다면 '기능'을 통해 접근하도록 유도하는 것.

   - 사용하는이유?

     → 안정성을 높이기 위해서

     → 데이터(변수 등)에 직접 접근하면 논리적 오류가 발생할 수 있음

          (e.g. 특정 멤버 변수에 양수의 값만 저장되도록 (if문을 사용해서) 메소드를 정의했는데,

            멤버 변수에 직접 접근해버리면 이런 조건을 모두 무시하고 음수값도 저장시킬 수 있는 문제가 생김)

     → 논리적 오류를 문법적 오류가 되도록 하는 방법 중 하나가 정보은닉임

   - 정보은닉 필요한 경우 예시

      → 아래와 같은 클래스가 있다고 가정하며, 양수만 입력 받도록 하고 싶다고 가정

      → 클래스 외부에서 number2 멤버 변수의 값을 음수로 변경해버리면, 컴파일 에러 없이 변경되어버림

      → 멤버 변수 number2가 외부에서 접근 가능하기 때문

package com.example.project1;

public class Calculator {
	int number1 = 0;
	public int number2 = 0; // 외부에서 접근 가능한 상황 연출 위해서 public 설정함
	char operator = ' '; // char 타입은 공백으로 초기화
	
	public Calculator(int n1, int n2, char op) {
		setExpression(n1, n2, op);
	}
	
	public void setExpression(int n1, int n2, char op) { 
		if((n1 < 0) || (n2 < 0)) {
			number1 = 0;
			number2 = 0;
			operator = ' ';
			System.out.println("양수를 입력하세요.");
			return;
		}
		number1 = n1;
		number2 = n2;
		operator = op;
	}
	
	public int getResult() {
		switch(operator) {
		case '+' :
			return (number1+number2);
		case '-' :
			return (number1-number2);
		case '*' :
			return (number1*number2);
		default :
			return 0; // 그 외에는 '0' 반환
		}
	}

}

   - 정보은닉 방법은 멤버 변수 앞에 'private' 키워드를 입력해주면 됨

      → 클래스 내부의 식구들은 해당 멤버 변수에 접근할 수 있지만 외부에서는 접근 불가능함

      → 만약 외부에서 private 멤버 변수에 접근하려고 하면 컴파일 에러가 발생함

 

※ 어떠한 멤버 변수의 값을 수정하기 위한 멤버 메소드를 'Setter'라고 함

※ 반대로 멤버 변수의 값을 가져오기 위한 멤버 메소드를 'Getter'라고 함

 

4. 접근수준 지시자

   - Java에서 제공하는 접근수준 지시자 : public / protected / default / private

   - private : '외부에서의 접근을 제한한다' 정도로 해석해봄

      → 보통 인스턴스 변수는 private로 선언하고, Getter와 Setter를 public으로 설정하는 경우가 많음

   - default : 'default'라는 키워드가 아님. 그냥 '어떠한 접근수준 지시자도 선언되지 않음' 정도로 생각하면 될 듯

   - 클래스에서 쓸 수 있는 접근수준 지시자와 인스턴스 변수/메소드에서 사용할 수 있는 접근수준 지시자가 다름

클래스 정의할 때 : public, default
인스턴스 멤버(변수/메소드) 선언할 때 : public, protected, default, private

 

4.1. 클래스 정의 시 접근수준 지시자

   - public과 default의 차이

      → public으로 선언하면? : 어디에서든지 인스턴스 생성 가능

      → default 라면? : 그 클래스가 묶여있는 패키지(package) 안에서만 인스턴스 생성이 가능함

      → 예시

public 선언된 "Plus 클래스"는 다른 소속의 패키지 안에서 사용해도 문제 없지만 default 선언된 "Multiple 클래스"는 본인이 소속된 패키지가 아닌 곳에서 사용하면 에러 발생함
예시에서 에러가 발생한 이유는 'Multiple 클래스'가 public으로 설정되지 않았기 때문 (혹은 Multiple과 Dan1 클래스를 같은 패키지에 두어야함)

 

   - 하나의 소스파일(*.java) 안에는 하나의 "public" 클래스만 둘 수 있음

      → JAVA에는 소스파일의 이름과 클래스 이름이 일치 해야하는 제약사항이 있음

      → public으로 선언된 클래스의 외부 노출도를 높이기 위해서

           (개발자는 소스파일의 이름만 보고도 어떤 public 클래스가 있는지 단번에 알 수 있음)

 

 

4.2. 인스턴스 멤버 선언 시 접근수준 지시자

   - public 선언 시 클래스 외부 어디에서든지 접근 가능

   - private 선언 시 클래스 외부에서 접근 불가능 , 클래스 내부에서 접근 가능

   - default 선언 시 "해당 인스턴스 멤버가 소속된 패키지 안에서는" 접근 가능

   - protected는 '상속'에 대해서 이해 해야지만 알 수 있음

 

 

4.2.1. protected 접근수준 지시자

   - 우선, Java는 패키지 선언이 안된 클래스들은 하나의 패키지로 묶어버림 ('디폴트 패키지'라고 함)

     → 패키지를 선언하지 않은 부모 클래스와 자식 클래스도 마찬가지임

디폴트 패키지

   - 그리고 상속 관계에 대해서 간단히 이해해야함

      → 부모 클래스의 인스턴스 멤버를 자식 클래스가 물려 받는 것과 비슷한 개념임

      → (코드 상에서는 보이지 않아도) 자식 클래스 안에 '부모 클래스의 인스턴스 멤버'가 선언된 것과 동일한 상태가 됨

 

   - 아래와 같은 상속 관계의 두 클래스가 있다고 가정

      → 일반적으로 A는 B의 'num' 인스턴스 변수에 접근할 수 없어야 하는게 맞음

           (왜냐하면, A에 있는 num이 B의 것인지 명시해주지 않았기 때문)

            하지만, 둘은 같은 패키지로 묶이고(= 디폴트 패키지) 상속 관계이기 때문에 num에 접근이 가능함

public class B {
	int num;	// 접근수준 지시자는 default
    ...
}
public class A extends B { // A는 B(부모)의 인스턴스 멤버를 상속받음
	public void init(int n) {
        num = n; // B의 'num' 변수에 값을 넣으려는 의도
        ...
    }
}

 

 

   - 그렇다면 상속 관계이지만 '패키지가 다르다'면?

      → 아래 코드에서 A는 'num'에 접근할 수 없음

           (아무리 상속 관계라고 하더라도 B의 'num' 인스턴스 변수가 default로 선언되어 있고,

            두 클래스가 서로 다른 패키지에 속해있기 때문)

      → 이를 해결하려면 'protected' 지시자를 사용해야함

package com.example1.aaa;
// com.example1.aaa 패키지로 묶이게 됨

public class B {
	int num;	// 접근수준 지시자는 default
    ...
}
// 디폴트 패키지에 묶이게 됨

public class A extends B { // A는 B(부모)의 인스턴스 멤버를 상속받음
	public void init(int n) {
        num = n; // B의 'num' 변수에 값을 넣으려는 의도
        ...
    }
}

 

   - 즉, protected는 (같은 패키지로 묶이지 않았더라도) 상속 관계에 있는 클래스에 접근을 하도록 해주는 지시자임

     → 아래 코드는 실행이 가능함 ('num'이 protected로 선언되었기 때문)

package com.example1.aaa;
// com.example1.aaa 패키지로 묶이게 됨

public class B {
	protected int num; // 접근수준 지시자는 protected (상속 관계의 클래스에서 접근 가능함)
    ...
}
// 디폴트 패키지에 묶이게 됨

public class A extends B { // A는 B(부모)의 인스턴스 멤버를 상속받음
	public A(int n) {
    	super();
        this.num = n; // B의 'num' 변수에 값을 넣으려는 의도
        ...
    }
}

 

※ 접근수준 지시자 정리

   # 클래스 정의 시 사용할 수 있는 접근수준 지시자 : public, default

       - public : 어디에서든지 인스턴스 생성 가능

       - default : 해당 클래스가 속한 패키지 안의 클래스에서만 인스턴스 생성 가능

 

   # 인스턴스 멤버 선언 시 사용할 수 있는 접근수준 지시자 : public, protected, default, private

       - public : 어디에서든지 인스턴스 멤버에 접근 가능 (클래스 내부에서도, 클래스 외부에서도 모두 가능)

       - protected : 같은 패키지 안에서 접근 가능 + (부모 클래스와 자식 클래스가 같은 패키지 안에 속하지 않았더라도) 상속 관계의 클래스에서 접근 가능

       - default : 동일한 패키지 안에 있는 클래스에서만 접근 가능

       - private : 클래스 내부에서만 접근 가능

 

Comments