1. 패키지
패키지는 파일 시스템의 폴더 역할을 한다. 우리는 패키지를 통해 여러 자바 프로그램 파일을 체계적으로 관리할 수 있다.
또한 패키지는 클래스의 식별자 역할을 한다. 사실 클래스의 전체 이름은 '클래스가 속한 패키지 + 클래스'이기 때문에 이러한 것이 가능하다. 예를 들어 아래와 같이 동일한 이름의 클래스가 두개 존재하더라도 그 클래스가 속한 패키지를 통해 두 클래스를 다르게 식별할 수 있다.
- 상위패키지A.하위패키지B.Cumputer.class
- 상위패키지A.하위패키지C.Cumputer.class
특정 클래스를 작성할 때 해당 클래스가 어떤 패키지에 속할지 결정해주는 것을 패키지 선언이라고 한다.
패키지 선언은 자바파일의 제일 상위에 작성해주면 된다.
- package 상위패키지.하위패키지;
또한 클래스는 자신이 속한 패키지에 귀속되기 때문에, 클래스만 떼어내서 다른 곳에 옮긴다면 해당 클래스는 사용할 수 없다. 클래스가 사용되기 위해서는 패키지와 함께 이동되어야한다.
패키지 이름은 자유지만, 지켜야할 규칙들이 있다.
- 숫자로 시작되서는 안됨.
- 특수문자는 _, $만 사용 가능함.
- java로 시작할 수 없음.
- 모두 소문자로 작성하는 것이 관례임.
1) import문
만약 불러오고자하는 클래스 또는 인터페이스가 다른 패키지에 속해 있다면, import문을 통해 해당 클래스를 사용할 것이라고 컴파일러에게 인식해줘야한다.
import문은 패키지 선언과 클래스 선언 사이에 작성해야하며, 선언 방법은 아래와 같다.
- import 상위패키지.하위패키지.클래스이름;
- import 상위패키지.하위패키지.*;
import문 선언에서 주의할 점은 상위패키지를 선언했다고해서 하위패키지까지 선언되는 것은 아니라는 것이다.
예를 들어 상위패키지A에 Apple.class가 존재하고, 상위패키지A의 하위패키지B에 Banana.class가 존재한다고 가정하자.
import 상위패키지A.*;라고 선언할 경우, Apple.class는 정상적으로 인식된다.
그러나 Banana.class는 상위패키지A의 직속 클래스가 아닌 하위패키지B의 직속 클래스이기 때문에 인식되지 않는다.
Banana.class의 인식을 위해서는 import 상위패키지A.하위패키지B.*;라고 별도의 선언이 필요하다.
그리고 import문과는 무관하게 외부 클래스 사용 시 클래스 전체명(패키지+클래스)을 사용해야하는 경우가 있다.
만약 두 패키지가 import되었고, 두 패키지에 동일한 이름의 클래스가 존재한다고 가정하자.
이런 경우에는 클래스 전체명을 통해 컴파일러에게 우리가 사용할 클래스가 무엇인지 정확히 인식시켜줘야한다.
아래의 예제 코드에서 Tire 클래스는 hankook 패키지, kumho 패키지에 모두 존재한다.
그래서 Tire 클래스를 사용하기 위해 클래스 전체명을 통해 컴파일러에게 인식해주고 있다.
package sec06.exam02.hankook;
public class Tire {
}
package sec06.exam02.kumho;
public class Tire {
}
package sec06.exam02.hyundai;
public class Engine {
}
package sec06.exam02.mycompany;
import sec06.exam02.hankook.*;
import sec06.exam02.kumho.*;
import sec06.exam02.hyundai.Engine;
public class Car {
//필드
Engine engine = new Engine();
SnowTire tire1 = new SnowTire();
BigWidthTire tire2 = new BigWidthTire();
sec06.exam02.hankook.Tire tire3 = new sec06.exam02.hankook.Tire(); //다른 패키지에 있는 동일한 이름의 클래스를 사용할 경우, 올바른 인식을 위해 전체클래스명(패키지 + 클래스)을 작성해야한다.
sec06.exam02.kumho.Tire tire4 = new sec06.exam02.kumho.Tire(); //다른 패키지에 있는 동일한 이름의 클래스를 사용할 경우, 올바른 인식을 위해 전체클래스명(패키지 + 클래스)을 작성해야한다.
}
2. 접근 제한자
상황에 따라서 클래스, 인스턴스, 그리고 그것들의 멤버에 대한 외부의 접근을 차단할 필요가 있다.
이때 사용되는 것이 접근 제한자이다.
접근 제한자에는 public, proteced, default, private 접근 제한자가 있다.
- public 접근 제한자: 외부 클래스에서 자유롭게 접근 가능하다.
- protected 접근 제한자: 같은 패키지의 클래스 또는 자식 클래스에서 접근 가능하다.
- default 접근 제한자: 기본 접근 제한자. 다른 접근 제한자를 선언하지 않으면 적용된다. 같은 패지키의 클래스에서 접근 가능하다.
- private 접근 제한자: 외부에서 접근 불가능하다.
1) 클래스의 접근 제한
클래스를 선언할 때 해당 클래스를 동일 패키지 내에서만 사용할 지, 외부 패키지에서도 사용할 지를 결정해야한다.
이때 사용되는 접근 제한자가 public, default이다.
아래의 예제 코드에서 클래스A는 default 접근 제한이기 때문에 동일한 패키지에 속한 클래스B에서는 사용 가능하지만, 다른 패키지에 속한 클래스C에서는 사용할 수 없다.
반면에 클래스B는 public 접근 제한이기 때문에 다른 패키지에 속한 클래스C에서도 사용 가능하다.
package sec06.exam03.package1;
class A {
}
package sec06.exam03.package1;
public class B {
A a; //A클래스는 default 접근제한이므로 동일한 패키지 내에서 접근 가능하다.
}
package sec06.exam03.package2;
import sec06.exam03.package1.*;
public class C {
//A a; default 접근제한이기 때문에 다른 패키지에서 접근이 불가능하다.
B b;
}
2) 생성자의 접근 제한자
생성자의 접근 제한자를 통해 생성자를 어디에서 호출 가능하게 할 것인가를 결정할 수 있다.
public, protected, default, private을 생성자의 접근 제한자로 사용할 수 있다.
클래스에 자동으로 추가되는 기본 생성자의 경우, 해당 클래스의 접근 제한자가 동일하게 적용된다.
아래의 예제 코드에는 클래스A가 있으며 클래스A는 public생성자, default생성자, private생성자를 가지고 있다.
동일 패키지에 속한 클래스B에서는 public생성자, default생성자에만 접근 가능하며 private생성자에는 접근 불가능하다.
다른 패키지에 속한 클래스C에서는 public생성자에만 접근 가능하다.
package sec06.exam04.package1;
public class A {
//필드
A a1 = new A(true);
A a2 = new A(1);
A a3 = new A("문자열");
public A(boolean b) {} //public 접근제한
A(int b) {} //default 접근제한
private A(String s) {} //private 접근제한
}
package sec06.exam04.package1;
public class B {
//필드
A a1 = new A(true);
A a2 = new A(1);
//A a3 = new A("문자열"); private 접근제한이므로 동일 패키지에서도 사용 불가.
}
package sec06.exam04.package2;
import sec06.exam04.package1.*;
public class C {
//필드
A a1 = new A(true);
//A a2 = new A(1); // default 접근제한이므로 다른 패키지에서 사용 불가.
//A a3 = new A("문자열"); // private 접근제한이므로 다른 패키지에서 사용 불가.
}
3) 필드와 메소드의 접근 제한
필드와 메소드 또한 접근 제한자를 통해 어디서 호출 가능하게 할 것인지를 결정할 수 있다.
생성자와 마찬가지로 public, protected, default, private 접근 제한자를 사용할 수 있다.
아래의 예제코드에서 클래스A는 public 접근 제한자를 가진 field1과 method1, default 접근 제한자를 가진 field2와 method2, private 접근 제한자를 가진 field3와 method3를 가지고 있다.
클래스A 내부에서는 모든 필드와 메소드에 접근이 가능하다.
동일 패키지에 속한 클래스B에서는 field1, 2와 method1, 2에 접근 가능하다.
다른 패키지에 속한 클래스C에서는 field1과 method1에 접근 가능하다.
package sec06.exam05.package1;
public class A {
//필드
public int field1;
int field2;
private int field3;
//생성자
public A() {
field1 = 1;
field2 = 1;
field3 = 1;
method1();
method2();
method3();
}
//메소드
public void method1() {}
void method2() {}
private void method3() {}
}
package sec06.exam05.package1;
public class B {
public B() {
A a = new A();
a.field1 = 1;
a.field2 = 1;
//a.field3 = 1; private 접근제한은 클래스 내부에서만 사용가능.
a.method1();
a.method2();
//a.method3(); private 접근제한은 클래스 내부에서만 사용가능.
}
}
package sec06.exam05.package2;
import sec06.exam05.package1.A;
public class C {
public C() {
A a = new A();
a.field1 = 1;
//a.field2 = 1; default 접근제한은 다른 패키지에서 접근 불가.
//a.field3 = 1; private 접근제한은 다른 패키지에서 접근 불가.
a.method1();
//a.method2(); default 접근제한은 다른 패키지에서 접근 불가.
//a.method3(); private 접근제한은 다른 패키지에서 접근 불가.
}
}
3. Getter와 Setter 메소드
일반적으로 객체 지향 프로그래밍에서는 객체의 필드에 직접적으로 접근하는 것을 허용하지 않는다. 외부의 직접적인 접근으로 인해 객체의 무결성이 사라질 수 있기 때문이다.
그래서 객체 지향 프로그래밍에서는 필드 자체에는 외부에서 접근할 수 없게 제한하고, 별도의 메소드를 통해 필드에 접근할 수 있도록 한다. 메소드의 매개값이 유효한 값인지 사전에 검증하고, 유효한 값일 때만 필드에 대입될 수 있도록 하여 객체의 무결성을 유지할 수 있다. 이때 사용되는 메소드를 Setter라고 한다.
또한 외부에서 객체의 데이터를 읽을 때도 메소드를 거쳐 읽을 수 있도록 하는 것이 좋다. 필드값이 직접 사용되기에는 부적절한 경우가 있으며, 이런 경우에는 메소드를 통해 적절하게 가공된 필드값을 불러올 수 있다. 이때 사용되는 메소드가 Getter다.
Getter와 Setter의 선언 방법은 아래와 같다.
- private 타입 fieldName; //필드에 private 접근제한자를 적용
- public 리턴타입(=필드타입) getFieldName() {}; //Getter 메소드 작성. public.
- public void setFieldName(필드타입 fieldName) { this.fieldName = fieldName }; //Setter 메소드 작성. public.
아래의 예제 코드에는 클래스Car의 speed와 stop필드에 대한 Getter와 Setter가 작성되어있다.
Getter인 getSpeed와 getStop은 각각 speed와 stop의 값을 반환한다.
Setter인 setSpeed와 setStop은 speed와 stop의 필드값을 할당해주는데, 여기서 주목할 점은 setSpeed에서 if문을 통해 setSpeed의 매개값이 speed의 값으로 유효한 값인지를 판단하여 처리해주는 것이다.
package sec06.exam06;
public class Car {
//필드
private int speed;
private boolean stop;
//생성자
//메소드
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
if(speed < 0) {
this.speed = 0;
return;
} else {
this.speed = speed;
return;
}
}
public boolean isStop() {
return stop;
}
public void setStop(boolean stop) {
this.stop = stop;
this.speed = 0;
}
}
package sec06.exam06;
public class CarExample {
public static void main(String[] args) {
Car myCar = new Car();
myCar.setSpeed(-50);
System.out.println("현재 속도: " + myCar.getSpeed());
myCar.setSpeed(50);
System.out.println("현재 속도: " + myCar.getSpeed());
if(!myCar.isStop()) {
myCar.setStop(true);
}
System.out.println("현재 속도: " + myCar.getSpeed());
}
}
출처: 혼자 공부하는 자바(신용권)
'Java > Java' 카테고리의 다른 글
상속_타입 변환과 다형성 (0) | 2022.02.21 |
---|---|
상속_상속 (0) | 2022.02.16 |
클래스_인스턴스 멤버와 정적멤버 (0) | 2021.11.28 |
클래스_메소드 (0) | 2021.11.14 |
클래스_생성자 (0) | 2021.11.14 |