공부/전자컴퓨터공학

Java 캡슐화란? 자바 기초 배우기

AhJustC 2024. 5. 27. 21:21
반응형
캡슐화(Encapsulation)

캡슐화란 특정 객체 안에 관련된 속성과 기능을 하나의 캡슐(capsule)로 만들어 데이터를 외부로부터 보호하는 것을 말한다. 캡슐화를 하는 목적으로는 첫째로 데이터의 보호이며, 둘째로 내부적으로만 사용되는 데이터에 대한 불필요한 외부 노출을 방지하기 위함이다.

I

캡슐화의 가장 큰 장점은 정보 은닉(data hiding)에 있다고 할 수 있다. 즉, 외부로부터 객체의 속성과 기능이 함부로 변경되지 못하게 막고, 데이터가 변경되더라도 다른 객체에 영향을 주지 않기에 독립성을 확보할 수 있다. 더 나아가 유지보수와 코드 확장 시에도 오류를 최소화 할 수 있다. 캡슐화를 수행하기 위한 색심적인 수단으로 접근 제어자(Access Modifier)와 getter와 setter 메서드가 있다.

 

패키지(Package)

패키지란 특정한 목적을 공유하는 클래스와 인터페이스의 묶음을 의미한다. 패키지는 클래스들을 그룹 단위로 묶어 효과적으로 관리하기 위한 목적을 가지고 있다. 패키지는 물리적인 하나의 디렉토리(directory)이고, 하나의 패키지에 속한 클래스나 인터페이스 파일은 모두 해당 패키지에 속해있다. 더 나아가 이 디렉토리는 하나의 계층 구조를 가지고 있는데 계층 구조간 구분은 점(.)으로 표현한다. 패키지가 있는 경우 소스 코드의 첫번째 줄에는 반드시 package 패키지 명 이 표시 되어야 하고 선언이 없으면 이름 없는 패키지에 속하게 된다.

// 패키지를 생성했을 때
package practicepack.test; // 패키지 구문 포함. 패키지가 없다면 구문 필요없음

public class PackageEx {

}

자바에 기본적으로 포함된 패키지로는 java.lang, java.util, java.io, java.nio 등이 있다. 이렇게 패키지로 클래스를 묶는 것의 또 하나의 장점은 클래스의 충돌을 방지해주는 기능이 있다. 예를 들어 같은 이름의 클래스가 다른 패키지에 속해있다면 이름으로 있한 충돌은 발생하지 않는다.

 

Import 문

import 문은 다른 패키지 내의 클래스를 사용하기 위해 사용하며 일반적으로 패키지 구문과 클래스 문 사이에 작성한다. 예를 들면 import문 없이 다른 패키지의 클래스를 사용하기 위해서는 아래와 같이 매번 패키지 명을 붙여주어야 하는데 import문을 사용하면 사전에 컴파일러에게 소스파일에 사용된 클래스에 대한 정보를 제공하여 이러한 번거로움을 덜어준다.

package practicepack.test;

public class ExampleImport {
		public int a = 10;
		public void print() {
			System.out.println("Import 문 테스트");
		}
}
package practicepack.test2; // import 문을 사용하지 않는 경우, 다른 패키지 클래스 사용방법

public class PackageImp {
		public static void main(String[] args) {
			practicepack.test.ExampleImport example = new practicepack.test.ExampleImport();
		}
}

위의 예시에서 import 문을 사용하지 않고 다른 패키지의 클래스를 사용하기 위해서 패키지 명을 모두 포함시켜 클래스 패키지에 대한 정보를 제공해야한다. import 문을 사용하면 아래와 같이 적을 수 있다.

import 패키지 명.클래스 명; 또는 import 패키지 명.*;

import 키워드를 써주고 패키지 명과 패키지 명을 생략하고자 하는 클래스명을 함께 써주면 된다.

이 방법을 적용하여 예제에 적용해보자

package practicepack.test;

public class ExampleImp {
    public int a = 10;
    public void print() {
        System.out.println("Import 문 테스트");
    }
}
package practicepack.test2; // import 문을 사용하는 경우

import practicepack.test.ExampleImp // import 문 작성

public class PackageImp {
		public static void main(String[] args) {
			ExampleImp x = new ExampleImp(); // 이제 패키지 명을 생략 가능
		}
}

 

제어자

자바 프로그래밍에서 제어자는 클래스, 필드, 메서드, 생성자 등에 부가적인 의미를 부여하는 키워드이다. 제어자는 크게 접근 제어자와 기타 제어자로 구분할 수 있다.

  • 접근 제어자: public, protected, (default), private
  • 기타 제어자: static, final, abstract, native, transient, stnchronized 등
접근 제어자(Access Modifier)

접근 제어자를 사용하면 클래스의 외부로으 ㅣ불필요한 데이터 노출을 방지(data hiding) 할 수 있고, 외부로부터 데이터가 임의로 변경되지 않도록 막을 수 있다. 접근 제어자로는 아래의 4가지가 있다.

접근 제어자 접근 제한 범위
private 동일 클래스에서만 접근 가능
default 동일 패키지 내에서만 접근 가능
protected 동일 패키지 + 다른 패키지의 하위 클래스에서 접근 가능
public 접근 제한 없음

위의 제어자를 접근 제한 범위에 따라 표현하면 public>protected>default>private 순으로 정리할 수 있다.

package package1; // 패키지명 package1

//파일명: Parent.java
class Test { // Test 클래스의 접근 제어자는 default
    public static void main(String[] args) {
        Parent p = new Parent();

//        System.out.println(p.a); // 동일 클래스가 아니기 때문에 에러발생!
        System.out.println(p.b);
        System.out.println(p.c);
        System.out.println(p.d);
    }
}

public class Parent { // Parent 클래스의 접근 제어자는 public
    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

위의 코드를 해석해보자. Parent 클래스에서 a, b, c, d 모두 에러 없이 접근 가능한 것을 알 수 있다. 반면 위의 Test 클래스에서는 객체를 생성하여 접근을 시도 했을 때 private 접근 제어자가 쓰인 a에는 접근이 불가하여 에러가 발생한다.

결론적으로 다시 정리하면 접근제어자를 통해 외부로부터의 데이터를 보호하고 불필요하게 데이터가 노출되는 것을 막을 수 있다.

반응형
getter와 setter 메서드

provate 접근제어자가 포함되어 있는 객체의 변수의 데이터나 값을 추가하거나 수정하고 싶을 때 getter와 setter 메서드를 사용할 수 있다. 아래의 예시를 봐보자.

public class GetterSetterTest {
    public static void main(String[] args) {
        Worker w = new Worker();
        w.setName("김코딩");
        w.setAge(30);
        w.setId(5);

        String name = w.getName();
        System.out.println("근로자의 이름은 " + name);
        int age = w.getAge();
        System.out.println("근로자의 나이는 " + age);
        int id = w.getId();
        System.out.println("근로자의 ID는 " + id);
    }
}

class Worker {
    private String name; // 변수의 은닉화. 외부로부터 접근 불가
    private int age;
    private int id;

    public String getName() { // 멤버변수의 값
        return name;
    }

    public void setName(String name) { // 멤버변수의 값 변경
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age < 1) return;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

// 출력값
근로자의 이름은 김코딩
근로자의 나이는 30
근로자의 ID는 5

먼저 setter 메서드는 외부에서 메서드에 접근하여 조건에 맞을 경우 데이터 값을 변경할 수 있게 해주고 일반적으로 메서드명에 set- 을 붙여서 정의한다. 예시를 보면 이름을 변경하기 위해 setName()이라는 메서드를 사용하고 있음을 알 수 있다. 한편 getter 메서드는 이렇게 설정한 변수값을 읽어오는 데 사용하는 메서드이다. 위의 예시에서 볼 수 있듯이 get-을 메서드 명 앞에 붙여서 사용한다. 위의 예시에서 Worker 클래스를 기반으로 객체 인스턴스를 생성해주고 같은 타입을 가지고 있는 참조변수 w에 담았다. w의 setter 메서드를 사용하여 이름, 나이, 아이디에 대한 데이터 값을 저장하고 getter 메서드를 통해 해당 데이터 값을 불러와 변수에 담아 출력하고 있다.

이렇게 setter와 getter 메서드를 활용하면 데이터를 효과적으로 보호하면서도 의도하는 값으로 값을 변경하여 캡슐화를 보다 더 효과적으로 달성할 수 있다.

반응형