
캡슐화란 OOP에서 객체의 데이터와 기능을 하나로 묶고 외부에 노출되지 않도록 숨김 처리하는 것을 말한다.
데이터 보호와 내부적으로 사용되는 데이터에 대한 불필요한 외부 노출을 방지할 수 있다.
캡슐화의 가장 큰 이점으로써, 외부로부터 객체의 속성이나 기능이 변경되지 못하게 막고, 데이터가 변경되더라도 다른 객체에 영향을 주지 않는 독립성을 갖는다.
따라서 OOP의 장점인 코드의 유지보수를 쉽게 하고 코드를 변경하더라도 오류의 범위를 최소화할 수 있다.
패키지 (Package)
패키지는 == 클래스와 인터페이스의 묶음
클래스가 관련된 속성과 기능을 묶어 관리하듯이 패키지는 그런 클래스들을 그룹 단위로 다시 묶어준다.
따라서 패키지를 통해 클래스의 관리를 쉽고 효과적으로 할 수 있다. 내가 폴더에 관련 파일들을 모아놓은 거라 같다고 보면 될 듯.
지금까지 단순하게 클래스 이름으로만 클래스를 구분했지만, 실제로는 클래스의 full name은 패키지 명을 포함한다.
예를 들어 String 클래스의 실제 이름은 java.lang.String이다. java.lang 패키지에 속한 String 클래스라는 뜻인데 가끔 에러 창에 java.lang 어쩌고 하는 문구가 나올 때가 있었는데 패키지와 관련된 무언가 였을 것이다.
이런 이유덕에 같은 이름의 클래스 파일이라도 각자 다른 패키지에 포함되어 있다면 패키지 명으로 구별할 수 있다.
클래스가 물리적인. class 파일 이듯이 패키지는 물리적인 하나의 디렉토리 (폴더)이다. 따라서 A라는 패키지 안에 있는 a라는 클래스는 A폴더 안에 존재해야 한다. 폴더 안에 하위 폴더가 있듯이 패키지도 하위 패키지를 가질 수 있다. 위의 java.lang 패키지는 java라는 패키지 안에 lang이라는 하위 패키지가 있다는 말이 된다.
중요 포인트는 다음과 같다.
1. 하나의 소스파일에는 첫 번째 문장으로써 단 한 번의 패키지 선언만 할 수 있다.
2. 모든 클래스는 반드시 하나의 패키지 안에 속해야 한다.
3. 패키지는 점(. )으로 계층구조를 구성 가능하다. 예) java.lang (java - 상위패키지 / lang - 하위패키지)
4. 패키지는 물리적으로 클래스 파일(. class)을 포함하는 하나의 디렉토리 이다.
**2번에 추가를 좀 더 해보자면 여지까지 소스파일에 패키지를 선언하지 않아도 실행이 되었다. 이는 자바에서 기본적으로 '이름 없는 패키지(unnamed package)'를 자동으로 선언해주기 때문이다. 따라서 결국 패키지에 포함된 것이나 마찬가지 상황이 된다.
패키지의 선언
packege 패키지명;
e.g)
package goginara.brisket; (고기나라 패키지 안의 brisket 패키지)
//패키지 명은 보통 소문자로 쓴다
패키지의 선언은 반드시 소스파일에서 주석과 공백을 제외한 첫 번째 문장 이어야 하고 하나만 선언할 수 있다.
import 문
다른 패키지 내의 클래스를 사용하기 위해 사용된다. (성능에 영향이 전혀 없다. 컴파일 시간만 조금 더 걸릴 뿐)
즉 컴파일러에게 소스파일에 사용된 클래스의 패키지에 대한 정보를 제공하기 때문에 매번 패키지명을 붙여 작성할 필요가 줄어든다.
import 패키지명.클래스명; //해당 클래스만 포함
or
import 패키지명.*; //패키지 안의 모든 클래스 포함
패키지명.* 은 여러 클래스를 사용할 때 편리하지만 단점으로는 import 한 패키지의 수가 많을 경우 어느 클래스가 어느 패키지에 속하는지 확인이 힘들어진다.
또 주의할 점으로, import문에서 클래스명 대신 .*를 쓰는 것이 하위 패키지의 클래스까지 포함하지 않는다. 해당 패키지의 클래스만 불러온다.
import 상위패키지명.*; //상위패키지의 모든 클래스 import (하위패키지 클래스 포함 X)
import 하위패키지명.*; //하위패키지의 모든 클래스 import
+tip
import로 패키지를 불러와야 한다고 했는데 java.lang 패키지는 import를 하지 않아도 해당 클래스를 쓸 수 있었다.
그 이유는 매우 자주 쓰이는 중요한 클래스들이 모인 패키지 이기 때문에 저절로 아래처럼 선언이 되어있다.
import java.lang.*;
static import문
import로 클래스의 패키지명을 생략할 수 있는 것처럼, static import문을 쓰면 static 멤버를 호출할 때 해당 클래스 이름을 생략할 숭 있다. 코드도 간결해지면서 특정 클래스의 static멤버를 자주 쓴다면 아주 편리하게 쓸 수 있을 것이다.
import static java.lang.Integer.*;
import static java.lang.Math.random;
import static java.lang.System.out;
예를 들어 위와 같이 static import를 선언하였다면
System.out.println(Math.random());
위의 코드를 아래처럼 쓸 수 있다.
out.println(random());
제어자(Modifier)
제어자는 클래스, 필드, 메서드, 생성자 등에 부가적인 의미를 부여하는 키워드이다.
제어자의 종류는 크게 접근제어자 / 그 외의 제어자로 나뉜다.
접근 제어자 :
public protected default private
그 외 제어자 :
static final abstract native transient synchronized volatile strictfp
제어자는 하나의 대상에 대해 여러 제어자를 조합해 사용하는 것이 가능하다.
예) public static void main처럼
단, 접근 제어자는 한 번에 네 가지 중 하나만 선택 가능하다.
접근 제어자
접근 제어자는 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하게 제한하는 역할을 한다.
캡슐화의 데이터 보호 측면에서 핵심적인 부분으로, 접근 제어자를 통해:
1. 외부로 불필요한 데이터 노출을 막을 수 있고
2. 데이터가 임의로 변경되지 않도록 보호할 수 있다
대상에 따라 사용 가능한 접근 제어자
대상 | 사용가능한 접근 제어자 |
클래스 | public, default |
메서드 | public, protected, default, private |
멤버변수 | |
지역변수 | 사용 불가 |
접근 제어자의 제한 범위
접근 제어자 | 접근 제한 범위 |
private | 같은 클래스 내에서만 접근 가능 |
default | 같은 패키지 내에서만 접근 가능 |
protected | 같은 패키지 내에서 + 다른 패키지의 하위 클래스 에서 접근 가능 |
public | 접근제한 X |
접근 제어자 | 같은 클래스 | 같은 패키지 | 다른 패키지 - 하위 클래스 |
같은 패키지 외 |
public | O | O | O | O |
protected | O | O | O | |
default | O | O | ||
private | O |
getter( ) / setter( )
만약 캡슐화를 하면서도 데이터의 변경을 하고 싶다면, 예를 들어 private이 붙은 객체의 변숫값을 외부에서 변경하고 싶을 때 getter( )와 setter( ) 메서드를 사용할 수 있다.
OOP에서 선호하는 방식은 외부에서 데이터로의 직접적인 접근을 막으면서, 메서드를 통해서 데이터에 접근할 수 있게 유도한다. 메서드 내에서 파라미터로 들어온 값을 if문 같은 조건문을 통해 필터링하여 설정할 수 있기 때문인데 이럴 때 setter 메서드가 쓰인다. 메서드 명 앞에 set-를 붙여 정의한다.
getter 메서드는 setter 메서드로 설정한 객체의 데이터를 외부에서 읽을 때 사용한다. 객체 외부에서 객체 필드 값에 접근하는 건 좋지 않은데 그 이유는 다음과 같다.
1. 객체의 데이터에 접근이 가능하면 메서드를 통해 만들어진 데이터의 의미가 없어진다.
2. 객체의 데이터는 어떤 메서드의 작업 결과를 누적해서 보존하는 경우가 많기에 함부로
접근이 가능하면 위험하다.
객체의 필드에 직접 접근하면 안되는 이유
이전에는 소프트웨어 개발에 있어서 오로지 문제를 해결하는 알고리즘(절차)를 중요하게 생각했다면 ( 절차지향 ) 소프트웨어가 다루는 데이터가 중요해지면서 로직과 데이터를 묶어둔 하나의
mititch.tistory.com
따라서 이상적인 OOP의 캡슐화 설계를 위해서는 필드 변수로 private를 사용하고 그 접근을 getter나 setter 메서드를 이용해야 하는 것 같다.
들어오는 값의 종류를 개발자의 통제범위 안에 둔다' 가 보안 관점의 캡슐화의 포인트라고 봐도 될 것이다.
아래 코드를 보면 setName / setAge / setIdnum 메서드로 파라미터 값이 들어가고 그걸 worker 클래스 안의 private 필드 변수에 넣는다. 그리고 getName, getAge, getIdnum 메서드로 private 필드 변수의 값을 리턴 값으로 반환한다.
public class capsule {
public static void main(String[] args) {
Worker worker = new Worker();
worker.setName("DANC");
worker.setAge(15);
worker.setIdnum(3311);
String name = worker.getName();
System.out.println("name: " + name );
System.out.println("Age: " + worker.getAge());
System.out.println("IdNum: " + worker.getIdnum());
}
}
class Worker
{
private String name; //Worker 클래스 외부에서 접근 불가
private int age;
private int idnum;
public String getName(){ //멤버변수의 값
return name;
}
public int getAge(){
return age;
}
public int getIdnum(){
return idnum;
}
public void setName(String name){ //Setter 멤버변수 값 변경
this.name = name;
}
public void setAge(int age){
if(age < 1) return;
this.age = age;
}
public void setIdnum(int id){
this.idnum = id;
}
}
name: DANC
Age: 15
IdNum: 3311
'programming > JAVA' 카테고리의 다른 글
Java - 추상화 / 인터페이스 (0) | 2022.05.15 |
---|---|
Java - 다형성 (0) | 2022.05.13 |
Java - 상속(Inheritance) (0) | 2022.05.12 |
Java - 생성자 (0) | 2022.05.11 |
Java - 메서드 (0) | 2022.05.10 |