[JAVA 심화]
개요
자바 객체지향 프로그래밍의 네 가지 주요 특성 중 두 번째 특성인 캡슐화에 대한 내용을 공부해봅시다.
학습목표
- 캡슐화의 핵심 개념과 목적을 이해한다.
- 패키지의 개념과 import문이 어떻게 사용되는 지 이해할 수 있다.
- 자바에서 캡슐화를 달성하기 위한 핵심적인 수단으로 접근제어자 네 가지를 이해하고, 각각의 접근 가능 범위를 설명할 수 있다.
- 데이터를 효과적으로 보호하기 위한 수단으로 getter/setter 메서드를 이해하고 사용할 수 있다.
캡슐화
캡슐화란 특정 객체 안에 관련된 속성과 기능을 하나의 캡슐(capsule)로 만들어 데이터를 외부로부터 보호하는 것을 말합니다.
이렇게 캡슐화를 해야하는 이유는 두 가지 목적이 있습니다.
- 데이터 보호
- 내부적으로만 사용되는 데이터에 대한 불필요한 외부 노출을 방지
캡슐화의 가장 큰 장점으로는 정보 은닉(data hiding)에 있습니다.
즉, 외부로부터 객체의 속성과 기능이 함부로 변경되지 못하게 하고 , 데이터가 변경되더라도 다른 객체에 영향을 주지 않0기에 독립성을 확보 수 있습니다.
게다가, 유지보수와 코드 확장 시에도 오류의 범위를 최소화할 수 있어서 효과적으로 코드를 유지보수하기에 용이합니다.
이번 글에서는 자바에서 캡슐화를 수행하기 위한 주요 수단으로 접근제어자(Access Modifier)와 getter와 setter메서드를 중심으로 공부해 보도록 하겠습니다.
먼저 접근제어자를 이해하기 위하여 선행되어야 하는 개념인 자바 패키지(package)에 대한 내용을 배우고, 접근제어자와 getter/setter 메서드 챕터를 통해 어떻게 자바 언어의 맥락에서 캡슐화를 달성할 수 있는지 확인해보도록 합시다.
패키지
패키지(package)란 특정한 목적을 공유하는 클래스와 인터페이스의 묶음을 의미합니다.
클래스를 정의할 때 관련있는 속성과 기능을 묶어 데이터들을 관리할 수 있었듯, 패키지는 클래스들을 그룹 단위로 묶어 효과적으로 관리하기 위한 목적을 가지고 있습니다.
자바에서 패키지는 물리적인 하나의 디렉토리(directory)이고, 하나의 패키지에 속한 클래스나 인터페이스 파일은 모두 해당 패키지에 속해있습니다.
디렉토리는 하나의 계층구조를 가지고 있는대, 계층 구조 간 구분은 dot(.)으로 표현됩니다.
마지막으로, 패키지가 있는 경우 소스 코드 첫 번째 줄에 반드시 package 패키지명이 표시되어야 하고, 만약 패키지 선언이 없으면 이름없는 패키지에 속하게 됩니다.
아래의 예시 코드를 확인해봅시다.
pacage practicepack.test;
public class PackageEX {
}
자바에 기본적으로 포함되어있는 대표적인 패키지로는 자바의 기본 클래스들을 모아 놓은 java.lang, 확장 클래스를 묶어 놓은 java.util, 자바의 입출력과 관련된 클래스를 묶어놓은 java.io와 java.nio 등이 있습니다.
예를들어, 주로 사용되는 String class의 실제 이름은 java.lang.Stirng인데, 여기서 java.lang은 패키지명을 나타내고 dot(.)을 사용하여 디렉터리 계층구조를 나타내고 있습니다.
이렇게 패키지로 클래스를 묶는 것은 클래스의 충돌을 방지해주는 기능이 있습니다.
Import 문
import문은 다른 패키지 내의 클래스를 사용하기 위해 사용합니다.
예를 들면, import문 없이 다른 패키지의 클래스를 사용하기 위해서는 매번 패키지명을 붙여 주어야 하는데,
import 문을 사용하면 사전에 컴파일러에게 소스파일에 사용된 클래스에 대한 정보를 제공하여 이러한 번거러움을 덜어줍니다.
아래의 예시 코드를 확인해보도록 하겠습니다.
pacage testpackage.test; //testpackage의 test 패키지
public class ExampleImport{
public int a = 5;
public void print() {
System.out.println("import test");
}
}
---------------------------------------------------------------
pakage testpackage.test2; //testpackage의 test2 패키지
//import문을 사용하지 않는 경우, 다른 패키지 클래스 사용방법
public class PackageImp {
public static void main(String[] args){
testpackage.test.ExampleImport example = new testpackage.test.ExampleImport();
}
}
-------------------------------------------------------------------
pakage testpackage.test3; //testpacakge의 test3 패키지
import testpackage.test.ExampleImp //import문을 사용하여 다른 패키지 클래스를 사용
public class packageImp {
public static void main(String[] args){
ExampleImport example = new ExampleImport(); // 패키지 명을 생략한 모습
}
}
위의 예시 코드를 보면, import문을 사용하지 않고 다른 패키지의 클래스를 사용하기 위해서는 패키지명을 모두 포함시켜서 클래스의 패키지에 대한 정보를 제공해야한다는 사실을 알 수 있습니다.
하지만 import문을 통해 패키지 명을 생략할 수 있음도 알 수 있습니다.
이러한 작업을 통해 훨씬 깔끔하고 번거롭지 않은 코드 작성이 가능해졌습니다.
접근 제어자
자바 프로그래밍에서 제어자는 class, feild, method, constructor 등에 부가적인 의미를 부여하는 키워드를 의미합니다.
자바에서 제어자는 크게 접근 제어자와 기타 제어자로 구분 할 수 있습니다.
접근 제어자 | public, protected, (default), private |
기타 제어자 | static, final, abstract, native, transient, synchronized 등 |
제어자는 class, feild, method, constructor에 주로 사용되며 하나의 대상에 여러 제어자를 사용할 수 있습니다.
그러나, 각 대상에 대해서 접근 제어자는 단 한번만 사용할 수 있습니다.
접근 제어자(Access Modifier)
접근 제어자를 사용하여 클래스 외부로의 불필요한 데이터 노출을 방지(data hiding)할 수 있고, 외부로부터 데이터가 임의 변경되지 않도록 막을 수 있습니다.
자바 접근 제어자로 다음의 네 가지가 있습니다.
접근 제어자 | 접근 제한 범위 |
private | 동일 클래스에서만 접근 가능 |
default | 동일 패키지 내에서만 접근 가능 |
protected | 동일 패키지 + 다른 패키지의 하위 클래스에서 접근 가능 |
public | 접근 제한 없음 |
접근 제한 범위를 표현하면
접근 제어자 | 클래스 내 | 패키지 내 | 다른 패키지의 하위 클래스 |
패키지 외 |
private | O | X | X | X |
default | O | X | X | X |
protected | O | O | O | X |
public | O | O | O | O |
접근 제한 범위를 위와같이 표현할 수있습니다. 이중 default의 경우 아무런 접근 제어자를 붙이지 않는 경우를 말합니다.
즉 변수명 앞에 아무런 접근제어자가 없는 경우에는 자동으로 해당 변수의 접근 제어자는 default가 됩니다.
예제 코드를 통해서 좀더 자세히 알 수 있습니다.
pacakge package1;
class Test {
public static void main(String[] args){
Parent p = new Parent();
// System.out.println(p.a); // Parent 클래스 내에서 a는 private로 선언되었다.
// Parent와 동일 클래스가 아니기 때문에 에러 발생!
System.out.println(p.b);
System.out.println(p.c);
System.out.println(p.d);
System.out.println(p.printEach());
}
}
public class Parent {
private int a = 1; //a,b,c,d에 각각 private, default, protected, public 접근 제어자 지정
int b = 2;
protected int c = 3;
public int d = 4;
public void printEach(){ // 동일 클래스이기 때문에 에러 발생하지 않음.
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
// 출력 값
2
3
4
1
2
3
4
예제 코드를 보면 아래 Parent 클래스는 a, b, c, d 모두 정상적으로 접근이 가능합니다.
이는 동일한 패키지에 동일한 클래스 내에 있기 때문에 가장 접근 제한이 엄격한 private 변수도 접근이 가능합니다.
반면 Test 클래스의 객체를 생성하여 접근을 시도했을 때는 private 접근 제어자가 있는 a는 호출이 불가하여 에러가 발생하는 모습을 확인할 수 있었습니다.
getter와 setter 메서드
앞서 우리는 자바에서 데이터 보호와 은닉을 위환 효과적인 방법으로 접근 제어자를 사용한다는것을 공부했습니다.
그렇다면 위의 코드 예제처럼 private 접근 제어자가 포함되어 있는 클래스의 데이터 값을 추가하거나 수정하고 싶을 경우에는 어떻게 할 수 있을까요?
이런 경우 getter와 setter 메서드를 사용할 수 있습니다.
코드 예제를 통해 알아보도록 합시다.
public class GetterSetterTest {
public static void main(String[] args){
Worker nom = new Worker();
nom.setName("놈");
String name = nom.getName();
System.out.println("제 이름은" + name + "입니다");
}
}
class Worker {
private String name;
public void getName(){
return name
}
public void setName(String name){
this.name = name;
}
}
// 출력 결과
제 이름은 놈입니다
위의 예제 코드를 보면 자바의 객체지향 프로그래밍에서 캡슐화를 통해 데이터를 보호하면서도 어떻게 데이터를 변경할 수 있는지 보여주고 있습니다.
먼저 setter 메서드는 외부에서 메서드에 접근하여 조건에 맞을 경우 데이터 값을 변경 가능하게 해주고 일반적으로 메서드명에 set- 을 붙여서 정의합니다.
예시를 보면 이름 값을 변경하기 위해 setName() 이라는 메서드를 사용하고 있음을 확인 할 수 있습니다.
getter 메서드는 설정한 변수 값을 읽어오는 데 사용하는 메서드입니다.
일반적으로 메서드명에 get- 을 붙여서 정의합니다.
예시를 보면 이름 값을 읽어오기 위해 getName() 이라는 메서드를 사용하고 있음을 확인할 수 있습니다.
이렇게 setter와 getter 메서드를 활용하여 데이터를 효과적으로 보호하면서도 의도하는 값으로 값을 변경하여 캡슐화를 보다 효과적으로 달성할 수 있습니다.
이번 글을 통하여 캡슐화에 대해 공부하여 보았습니다.
다음 시간에는 자바 객체지향의 네 가지 주요 특성 중 하나인 다형성에 대하여 공부해보도록 하겠습니다.
'JAVA > JAVA 이론' 카테고리의 다른 글
[JAVA 이론] 추상화(Abstraction) (0) | 2023.03.02 |
---|---|
[JAVA 이론] 다형성(polymorphism) (0) | 2023.03.01 |
[JAVA 이론] 상속(inheritance) (0) | 2023.02.27 |
[JAVA 이론] 내부 클래스(Inner Class) (0) | 2023.02.26 |
[JAVA 이론] 생성자(Constructor) (0) | 2023.02.25 |