220526~0530_혼.공.자 - Ch.6 클래스 2 (메소드, 멤버, 패키지 / 접근 제한자)

창고·2022년 10월 14일
0

티스토리에 저장했던 글을 옮겼습니다.
https://mrcocoball.tistory.com/76
https://mrcocoball.tistory.com/77
https://mrcocoball.tistory.com/78

Chapter 6. 클래스

4. 메소드

(1) 메소드란

  • 메소드(Method) : 객체의 동작에 해당하는 중괄호 블록
  • 메소드 선언은 선언부와 실행 블록으로 구성
  • 메소드 선언부를 메소드 시그니처(method signature)라고 함
  • 선언부와 실행 블록에는 다음 요소를 포함
  • 형태
리턴타입 메소드이름 ( [매개 변수 선언, ...] ) {

  실행할 코드를 작성하는 곳 (메소드 실행 블록)

}

// 리턴 타입 : 메소드가 리턴하는 결과의 타입을 표시
// 메소드 이름 : 메소드의 기능이 드러나도록 식별자 규칙에 맞게 이름을 지어줌
// 매개 변수 선언 : 메소드 실행 시 필요한 데이터를 받기 위한 변수를 선언
// 메소드 실행 블록 : 실행할 코드를 작성

(2) 메소드 선언

  • 메소드 선언은 선언부(리턴 타입, 메소드 이름, 매개 변수 선언)와 실행 블록으로 구성
  • 메소드 선언 예시 [전자계산기 객체 (Calculator)]
    • 전원 버튼
      • powerOn() 메소드
      • 전원을 켜고 끄는 기능 실행
      • 리턴값 없음
      • 리턴 타입으로 void 지정
    • 나누기 버튼
      • divide() 메소드
      • 나누는 기능
      • 리턴값은 나눗셈 결과
      • 리턴 타입으로 double 지정

1) 리턴 타입

  • 리턴값, 즉 메소드를 실행한 후의 결과값을 의미
  • 메소드는 리턴값이 있을 수도, 없을 수도 있으나 있을 경우엔 리턴 타입이 선언부에 명시되어 있어야 함
  • 위의 예시에서 전원 버튼 기능인 powerOn() 메소드는 리턴값이 없으나 divide() 메소드는 리턴값이 있음
void powerOn() { ... }
double divide( int x, int y ) { ... }
  • 리턴값이 있냐 없냐에 따라 메소드 호출 방법이 다름
powerOn();
double result = divide(10, 20);
  • 리턴 타입이 있다 해서 반드시 리턴값을 변수에 저장할 필요는 없음
    리턴값이 중요하지 않고 메소드 실행이 중요할 경우에는 변수를 선언하지 않고 메소드 호출이 가능
divide(10, 20);

2) 메소드 이름

  • 메소드 이름은 Java 식별자 규칙에 맞게 작성해야 하며 다음 사항에 주의
    • 숫자로 시작하면 안 되고 $와 _를 제외한 특수 문자 사용 금지
    • 관례적으로 메소드 이름은 소문자로 작성
    • 서로 다른 단어가 혼합된 이름이라면 뒤이어 오는 단어의 첫 글자는 대문자로 작성

3) 매개 변수 선언

  • 매개 변수는 메소드가 실행할 때 필요한 데이터를 외부로부터 받기 위해 사용
  • 메소드에서 매개 변수가 필요한 경우가 있고 없는 경우가 있음
    예) powerOn() 메소드는 매개 변수가 필요 없으나 divide() 메소드는 나눗셈에 사용될 두 숫자가 필요
  • 따라서 divide() 메소드는 매개 변수를 같이 선언
double divide( int x, int y) { ... }
  • 이렇게 선언된 메소드 호출 시 매개 변수 값을 반드시 주어야 함
double result = divide(10, 20);
  • 호출 시 넘겨준 매개값은 해당 위치의 매개 변수인 x, y에 저장되고 이 매개 변수를 통해 메소드 블록을 실행
  • 이 때 매개값은 반드시 매개 변수의 타입에 부합되는 값이어야 함 (int 이니까 int 값이나 int로 변환될 수 있는 값이어야 함)
  • 메소드 선언 정리
public class Calculator {

	// 메소드
 	void powerOn() { 
  		System.out.println("전원을 켭니다");
 	}

	int plus(int x, int y) {
    	int result = x + y;
  		return result;
 	}

 	double divide(int x, int y) {
  		double result = (double) x / (double) y; // int로 매개값을 받았으나 double로 변환
  		return result;
 	}

	void powerOff() {
  		System.out.println("전원을 끕니다");
 	}
}
  • 메소드 호출
public class CalculatorExample {
	public static void main(String[] args) {
    	Calculator myCalc = new Calculator();
        myCalc.powerOn();
        
        int result1 = myCalc.plus(5,6); // 메소드 호출 시점에서 매개값 지정하였음
        System.out.println("result1 : " + result1);
        
        byte x = 10;
        byte y = 4; 
        // 메소드 호출 전 x, y 변수 지정 후 값을 입력하였음
        double result2 = myCalc.divide(x,y); // 메소드 호출 시 x,y 매개값 지정하였음
        System.out.println("result2 : " + result2);
        myCalc.powerOff();
	}
}

4) 매개 변수의 개수를 모를 경우

  • 메소드의 매개 변수는 개수가 이미 정해져 있는 것이 일반적이나, 어떤 상황에서는 메소드 선언 시
    매개 변수의 개수를 알 수 없는 경우도 있음
  • 예를 들어 여러 개의 수를 모두 합산하는 메소드 선언 시 몇 개의 매개 변수가 입력될 지 알 수 없음
  • 이 경우 매개 변수를 배열 타입으로 선언

4-1) 매개 변수를 배열 타입으로 선언하는 경우

int sum1(int[] values) { }
  • sum1() 메소드 호출 시 배열을 넘겨줌으로서 배열의 항목 값들을 모두 전달할 수 있음.
    배열의 항목 수는 호출할 때 결정됨
int[] values = { 1, 2, 3 };
int result = sum1(values);
int result = sum1(new int[] {1,2,3,4,5});
  • 매개 변수를 배열 타입으로 선언 시 메소드를 호출하기 전에 배열을 생성해야 하는 불편함이 있음
  • 따라서 배열을 생성하지 않고 값의 목록만 넘겨주는 방법도 있음

4-2) 매개 변수를 ... 로 사용해서 선언

int sum2(int ... values) { }
  • ... 로 선언된 매개 변수의 값은 다음과 같이 메소드 호출 시 쉼표로 나열
int result = sum2(1, 2, 3);
int result = sum2(1, 2, 3, 4, 5);
  • ... 로 선언된 매개 변수는 배열 타입이므로 다음과 같이 배열을 직접 매개값으로 사용해도 됨
int[] values = {1,2,3};
int result = sum2(values);
int result = sum2(new int[] {1,2,3,4,5});

(3) 리턴(return)문

1) 리턴값이 있는 메소드

  • 메소드 선언에 리턴 타입이 있는 메소드는 반드시 리턴(return) 문을 사용, 리턴값을 지정
return 리턴값;
  • return 문 없을 시 컴파일 에러가 발생
  • return 문 실행 시 메소드는 즉시 종료됨 (return문 이후의 실행문은 조건문을 제외하면 결코 실행되지 않음.)
[Case 1]

int plus(int x, int y) {
	int result = x + y;
 	return result;
 	System.out.println(result);
    // Unreachble code, 컴파일 에러 (return 이후 메소드가 종료되지만 실행문이 남아있음)
[Case 2]
boolean isLeftGas() {
	if(gas==0} {
    	System.out.println("gas가 없습니다.");
        return false;
	}
 	System.out.println("gas가 있습니다.");
 	return true;
}

// 조건문이며 조건에 따라 false와 true를 실행하며 메소드가 종료되기 때문에 컴파일 에러가 발생하지 않음
  • 리턴값은 리턴 타입이거나 리턴 타입으로 변환되어야 함

2) 리턴값이 없는 메소드 : void

  • 리턴값이 없는 메소드는 리턴 타입으로 void를 사용
  • void로 선언된 메소드에서도 return문을 사용
return;

(4) 메소드 호출

  • 메소드는 클래스 내/외부의 호출에 의해 실행됨
  • 클래스 내부의 다른 메소드에서 호출할 경우에는 단순한 메소드 이름으로 호출
  • 클래스 외부에서 호출할 경우 클래스로부터 객체 생성 후 참조 변수를 이용하여 메소드 호출 (객체가 존재해야 메소드도 존재)
[외부]

void 외부메소드() { // 객체의 메소드를 호출해옴 (참조 변수 사용)

	Idol idol = new Idol();
 	idol.run(); 
 	idol.stop();
 	idol.singing();
}

[객체 내부]

// 메소드 선언
void run() { }
void stop() { }
void singing() { }

void 내부메소드() { // 위의 메소드 선언부를 호출함
	run();
 	stop();
 	singing();
}

1) 객체 내부에서 호출

  • 클래스 내부에서 다른 메소드를 호출할 경우 다음과 같은 형태로 작성
    메소드 (매개값, ...) ;
public class ClassName {

	void method1 (String p1, int p2) {

 	}

 	void method2 {
		method1("리코", 100)
	}
}

// method2 에서 method1을 호출
// method1의 매개값에 method2의 값을 대입하여 실행
  • 메소드가 리턴값이 없거나, 있어도 받고 싶지 않을 경우 위와 같이 모두 호출이 가능
  • 리턴값이 있는 메소드를 호출하고 리턴값을 받고 싶다면 다음과 같이 변수 선언 후 리턴값 대입
타입 변수 = 메소드(매개값, ...) ; (타입 -> 메소드 리턴 타입과 동일, 혹은 자동 타입 변환이 되어야 함)

public class ClassName {

	int method1(int x, int y) {
  		int result = x + y;
  		return result;
	}

 	void method2() {
  		int result1 = method1(10, 20); // result1에는 30 저장
  		double result2 = method1(10, 20); // result2에는 30.0 저장
	}
}

2) 객체 외부에서 호출

  • 외부 클래스에서 메소드를 호출하려면 클래스로부터 객체를 먼저 생성해야 함
    (메소드는 객체에 소속된 '멤버' 로 객체가 존재하지 않을 경우 메소드도 존재하지 않음)
클래스 참조변수 = new 클래스 (매개값, ...);
  • 객체 생성 후 참조 변수와 함께 도트(.) 연산자를 사용 메소드를 호출
    참조변수.메소드(매개값, ...); // 리턴값이 없거나, 있어도 리턴값을 받지 않을 경우 타입 변수 = 참조변수.메소드(매개값, ...); // 리턴값이 있고 리턴값을 받고 싶을 경우
  • 예제
[Idol 클래스]

Idol idol = new Idol();
idol.goLesson();
idol.run()
int energy = idol.getEnergy();

public class Idol {

	// 필드
 	int energy;

 	// 생성자

 	//메소드
 	int getEnergy() {
  		return energy;
 	}

 	void goLesson() {
  		System.out.println("레슨에 참여합니다.");
 	}

 	void run() {
  		for(int i=100; i=0; i-=10) {
   			energy = i;
   			System.out.println("레슨을 시작합니다. (에너지:" + energy + ")");
  		}
 	}
}

[외부 클래스]

public class IdolExample {
	public static void main(String[] args) {
    
		Idol idol = new Idol();
    	idol.goLesson();
    	idol.run();
    	int energy = idol.getEnergy();
    	System.out.println("현재 에너지: " + speed);
	}
}

(5) 메소드 오버로딩

  • 메소드 오버로딩(method overloading) : 클래스 내에 같은 이름의 메소드를 여러개 선언하는 것
  • 하나의 메소드 이름으로 여러 기능을 담는 것을 의미하며, 매개 변수의 타입, 개수, 순서 중 하나가 달라야 함
class 클래스 {
  리턴 타입           메소드이름 (타입변수, ...) {...}
       ↕ 상관없음      ↕ 동일      ↕ 매개변수의 타입, 개수, 순서가 달라야 함
  리턴 타입           메소드이름 (타입변수, ...) {...}
  • 예시
// int 계산용 method
int plus(int x, int y) {
	int result = x + y;
	return result;
}

// double 계산용 method
double plus(double x, double y) {
	double result = x + y;
	return result;
}
  • 매개 변수의 타입이 일치하지 않을 경우 자동 타입 변환이 가능한지 검사
    위에서 x가 int, y가 double일 경우 컴파일 에러가 발생하지 않고
    int x가 자동으로 double로 타입 변환 되어 계산됨
  • 매개 변수의 타입과 개수, 순서가 똑같을 경우 매개 변수 이름이 다르다고 해서 오버로딩 취급하지 않음
int divide(int x, int y) {...}
double divide(int a, int b) {...} // 오버로딩이 아니라 컴파일 에러 발생
  • 대표적인 메소드 오버로딩의 예 : System.out.println() 메소드
    println() 메소드 : 호출할 때 주어진 매개값의 타입에 따라 다음과 같이 오버로딩된 println() 메소드 중 하나를 호출
void println() {...}
void println(boolean x) {...}
void println(char x) {...}
void println(char[] x) {...}
void println(double x) {...}
void println(float x) {...}
void println(int x) {...}
void println(long x) {...}
void println(Object x) {...}
void println(Sring x) {...}
  • 예시
public class Calculator {
	// 정사각형의 넓이
 	double areaRectangle(double width) {
  		return width * width;
 	}

 	// 직사각형의 넓이
 	double areaRectangle(double width, double height) {
  		return width * heigth;
 	}
}

public class CalculatorExample {
	public static void main(String[] args) {
    
  		Calculator myCalcu = new Calculator():

 		// 정사각형 넓이
 		double result1 = myCalcu.areaRectangle(10);

 		// 직사각형 넓이
 		double result2 = myCalcu.areaRectangle(10, 20);

 		// 결과
 		System.out.println("정사각형 넓이=" + result1);
 		System.out.println("직사각형 넓이=" + result2);
 	}
}

5. 인스턴스 멤버와 정적 멤버

(1) 인스턴스 멤버와 정적 멤버

  • 클래스는 객체의 설계도이며 멤버(필드, 메소드) 는 객체에 포함되어 있어야 함
  • 다만 클래스로부터 객체(인스턴스)가 여러 개 만들어질 경우, 이 때 클래스 멤버들을 객체마다 모두 가질 필요가 있는가?
    -> 공통된 멤버가 있다면 이를 클래스 한곳에 위치시키고 객체들이 공유하는 것이 효율적
  • 이에 따라 자바는 클래스 멤버를 인스턴스 멤버와 정적 멤버로 구분해서 선언할 수 있게 함
    • 인스턴스 멤버 : 객체마다 가지고 있는 멤버
    • 정적 멤버 : 클래스에 위치시키고 객체들이 공유하는 멤버

(2) 인스턴스 멤버와 this

  • 인스턴스 멤버(instance member) : 객체(인스턴스) 생성 후 사용할 수 있는 필드 / 메소드. 각각 인스턴스 필드, 인스턴스 메소드로 부름
  • 객체에 소속된 멤버이기 때문에 객체 없이 사용 불가

1) 인스턴스 멤버 선언

  • 여태까지 배웠던 내용과 다르지 않음

2) this

  • 객체 내부에서도 인스턴스 멤버에 접근하기 위해 this를 사용할 수 있음
  • this는 주로 생성자와 메소드의 매개 변수 이름이 필드와 동일한 경우 인스턴스 멤버인 필드임을 명시하고자 할 때 사용
  • 예제
public class Idol {
	// 필드
 	String name;
 	int energy;

 	// 생성자
 	Idol(String name) {
  		this.name = name; // 매개 변수 name의 값을 필드 name에 저장
 	}

 	// 메소드
 	void setEnergy(int energy) {
  		this.energy = energy; // 매개 변수 energy의 값을 필드 energy에 저장
 	}

 	void run() {
  		for (int i=100; i = 0, i-=10) {
   			this.setEnergy(i);
   			System.out.println(this.name + "가 레슨을 합니다.(에너지:" + this.energy + ")");
   			// 객체, 메소드가 생성되어야 각각 name, energy를 사용할 수 있음
  		}
 	}
}

public class IdolExample {
	public static void main(String[] args) {
    
  	Idol idol = new Idol("리코");
  	Idol idol2 = new Idol("요시코")

  	idol.run();
  	idol2.run();
    
 	}
}

(3) 정적 멤버와 static

  • 정적 멤버(static member) : 클래스에 고정된 멤버로 객체를 생성하지 않고 사용할 수 있는 필드와 메소드
    각각 정적 필드, 정적 메소드라고 부름

1) 정적 멤버 선언

  • 필드와 메소드 선언 시 static 키워드 추가
public class 클래스 {
	// 정적 필드
 	static 타입 필드 [=초기값];

 	// 정적 메소드
 	static 리턴 타입 메소드 (매개변수선언, ...) { ... }

}
  • 정적 멤버는 클래스에 고정된 멤버이므로 클래스 로더가 클래스(바이트 코드)를 로딩해서 메소드 메모리 영역에 적재할 때 클래스별로 관리되며, 클래스의 로딩이 끝나면 바로 사용 가능
  • 인스턴스 / 정적 선언 기준 : 객체마다 가지고 있어야 한다면 인스턴스, 공용 데이터면 정적 필드
  • 예시
public class Calcualtor {

	String color; // 인스턴스 필드
 	static double pi = 3.141592; // 정적 필드
    
 	void setColor(String color) {this.color = color;} // 인스턴스 메소드
    static int plus(int x, int y) { return x + y; } // 정적 메소드
 	static int minus(int x, int y) { return x - y; } // 정적 메소드
 	static double circle(int x) { return pi * x * x } // 정적 메소드

// Calculator 클래스에서 공통으로 필요한 원주율 파이는 공용 데이터이므로 정적 필드로 선언
// 덧셈 / 뺄셈 메소드는 매개값을 가지고 공통된 연산 처리를 하는 것이므로 정적 메소드로 선언
// 단, Calculator 별로 색깔 / 모델이 다르고 이를 변경처리하는 작업에 대해서는 각각 인스턴스 필드, 메소드로 선언

2) 정적 멤버 사용

  • 클래스가 메모리로 로딩되면 정적 멤버를 바로 사용할 수 있으며 클래스 이름과 함께 도트(.) 연산자로 접근
클래스.필드;
클래스.메소드(매개값, ...);
  • 예시
public class Calculator {
	static double pi = 3.141592;
 	static int plus(int x, int y) {...}
 	static int minus(int x, int y) {...}
}

double result1 = 10 * 10 * Calculator.pi; // 클래스 이름으로 접근
int result2 = Calculator.plus(10,5); // 클래스 이름으로 접근
int result3 = Calculator.minus(10,5); // 클래스 이름으로 접근
  • 객체 참조 변수로도 접근은 가능하나, 클래스 이름으로 접근하는 것이 좋음 (이클립스 상에서는 경고 표시 뜸)
  • 예시
[클래스]

public class Calculator {
	// 정적 필드
 	static double pi = 3.141592; 

 	// 정적 메소드
 	static int plus(int x, int y) {
  		return x + y;
 	}

 	static int minus(int x, int y) {
  		return x - y;
 	}
}

[외부 호출]

public class CalcuatorExample {
	public static void main(String[] args) {
    
  		double result1 = 10 * 10 * Calculator.pi;
  		int result2 = Calculator.plus(10,5);
  		int result3 = Calculator.minus(10,5);

  		System.out.println("result1 : " + result1);
  		System.out.println("result2 : " + result2);
  		System.out.println("result3 : " + result3);
 	}
}

3) 정적 메소드 선언 시 주의점

  • 객체가 없어도 실행된다는 특징 때문에 정적 메소드 선언 시 이들 내부에 인스턴스 필드나 메소드를 사용할 수 없음
  • 객체 자신의 참조인 this 키워드도 사용이 불가
  • 이 경우 객체를 생성하고 참조를 해야 함
  • 예시
public class Car {

	int speed;

 	void run() {
  		System.out.println(speed + "으로 달립니다.");

 	public static void main(String[] args) {
  		// speed = 60; -> 인스턴스 필드이므로 main 메소드(정적)에서 바로 사용 불가
  		// run(); -> 인스턴스 메소드이므로 main 메소드(정적)에서 바로 사용 불가

  		Car myCar = new Car(); // 객체 생성 후 참조
  		myCar.speed = 60;
  		myCar.run();
 	}
}

(4) 싱글톤

  • 싱글톤(Singleton) : 전체 프로그램에서 단 하나의 객체만 만들도록 보장해야 하는 경우가 있는데
    이 때 하나만 생성되는 객체를 싱글톤이라고 함
  • 싱글톤을 만들려면 클래스 외부에서 new 연산자로 생성자를 호출할 수 없도록 막아야 함 (생성자를 호출한 만큼 객체가 생성되기 때문)
  • 생성자를 외부에서 호출할 수 없도록 하기 위해 생성자 앞에 private 접근 제한자를 붙임
  • 자신의 타입인 정적 필드를 하나 선언, 자신의 객체를 생성해 초기화
  • 클래스 내부에서는 new 연산자로 생성자 호출이 가능
  • 정적 필드도 private 접근 제한자를 붙여 외부에서 필드값을 변경하지 못하도록 막음
    대신 외부에서 호출할 수 있는 정적 메소드인 getInstance()를 선언, 정적 필드에서 참조하고 있는 자신의 객체를 리턴
  • 싱글톤을 만드는 코드
public class 클래스 {
	// 정적 필드
 	private static 클래스 singleton = new 클래스();

 	// 생성자
 	private 클래스() {}

	 // 정적 메소드
 	static 클래스 getInstance() {
  		return singleton;
 	}
}
  • 외부에서 객체를 얻는 유일한 방법은 getInstance() 메소드를 호출하는 방법 뿐이며
    해당 메소드는 단 하나의 객체만 리턴하므로 아래 코드에서 변수1과 변수2는 동일한 객체를 참조
클래스 변수1 = 클래스.getInstance();
클래스 변수2 = 클래스.getInstance();
  • 예제
[싱글톤]

public class Singleton {
	private static Singleton singleton = new Singleton();

	private Singleton() {}

 	static Singleton getInstance() {
  		return singleton;
 	}
}

[싱글톤 객체]

public class SingletonExample {
	public static void main(String[] args) {

  		/* 해당 부분 컴파일 에러
  		Singleton obj1 = new Singleton();
  		Singleton obj2 = new Singleton();
  		/* 해당 부분 컴파일 에러

  		Singleton obj1 = Singleton.getInstance();
  		Singleton obj2 = Singleton.getInstance();

  		if(obj1 == obj2) {
   			System.out.println("같은 Singleton 객체입니다.");
  		} else {
   			System.out.println("다른 Singleton 객체입니다.");
  		}
 	}
}

(5) final 필드와 상수

1) final 필드

  • final 필드 : 초기값이 저장되면 이것이 최종적인 값이 되어 프로그램 실행 도중 수정할 수 없음을 의미
  • final 필드 선언 방법
final 타입 필드 [=초기값];
  • final 필드의 초기값을 줄 수 있는 방법
    • 필드 선언 시 주는 방법 -> 단순 값일 경우 제일 간단함
    • 생성자에서 주는 방법 -> 복잡한 초기화 코드나 객체 생성 시 외부 데이터로 초기화해야 할 경우 사용
  • 생성자는 final 필드의 최종 초기화를 마쳐야 하는데 초기화되지 않은 final 필드를 그대로 남겨두면 컴파일 에러 발생
  • 예제
[final 필드 선언과 초기화]

public class Person {
	final String nation = "Korea"; // 선언 시 초기화
 	final String ssn; // 선언은 하였으나 생성자에서 초기화 진행
 	String name;

 	public Person (String ssn, String name) {
  		this.ssn = ssn; // 생성자에서 초기화 진행, 매개값 = final 필드 값
  		this.name = name;
 	}
}

[final 필드 테스트]

public class PersonExample {
	public static void main(String[] args) {
  		Person p1 = new Person("123456-1234567", "최쾅쾅");

  		System.out.println(p1.nation);
  		System.out.println(p1.ssn);
  		System.out.println(p1.name);

  		//p1.nation = "usa"; → final 필드값이라 변경 불가
  		//p1.ssn = "654321-7654321"; → final 필드값이라 변경 불가
  		p1.name = "최껑껑"
 	}
}

2) 상수

  • 상수(constant) : 불변의 값(static final)이며 final 필드이면서 static 해야 함
  • 즉, 객체마다 존재하지 않고 클래스에만 존재하며 초기값이 저장되면 변경될 수 없음
  • 형태
static final 타입 상수 = 초기값;
  • 상수 이름은 모두 대문자로 작성해야 하며, 서로 다른 단어가 혼합되어 있다면 언더바로 연결
static final double PI = 3.141592;
static final double EARTH_RADIUS = 6400;
static final double EARTH_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS

6. 패키지와 접근 제한자

(1) 패키지란

  • 클래스의 일부분으로, 클래스를 유일하게 만들어주는 식별자 역할을 함
  • 클래스 이름이 동일하더라도 패키지가 다르면 다른 클래스로 인식
  • 클래스의 전체 이름은 패키지 이름 + 클래스 이름
  • 패키지가 상,하위로 구분되어있을 경우 다음과 같이 표현
상위패키지.하위패키지.클래스
  • 예시
    1) 상위 패키지 com
    2-1) 하위 패키지 mycompany -> A.class
    2-2) 하위 패키지 yourcompany -> B.class
A 클래스의 전체 이름 = com.mycompany.A
B 클래스의 전체 이름 = com.yourcompany.B

(2) 패키지 선언

  • 패키지 선언 : 클래스 작성 시 해당 클래스가 어떤 패키지에 속할 것인지를 선언
  • 방법
package 상위패키지.하위패키지;

public class ClassName {...}
  • 패키지는 클래스의 일부. 클래스만 따로 복사해서 다른 곳으로 이동 시 클래스를 사용할 수 없음
  • 클래스를 이동해야 한다면 패키지 전체를 이동시켜야 함
  • 패키지 이름 명명 관련
    • 숫자로 시작해서는 안되고 _, $를 제외한 특수 문자 사용 불가
    • java로 시작하는 패키지는 자바 표준 API에서만 사용하므로 사용 불가
    • 모두 소문자로 작성
  • 패키지 이름 중복 방지 방법
    • 회사들 간에 패키지가 서로 중복되지 않게 회사의 도메인 이름으로 패키지를 만듬
    • 도메인 이름 역순으로 패키지 이름을 지어주며, 포괄적 이름이 상위 패키지가 되도록 하기 위함
    • 마지막에는 프로젝트 이름을 붙여줌
com.samsung.projectname
org.apache.projectname

(3) 접근 제한자

  • 접근 제한자(Access Modifier) : 접근을 제한하기 위해 사용됨
    (접근 : 클래스 및 인터페이스, 이들이 가지고 있는 멤버의 접근)
  • 종류
    • public 접근 제한자 : 외부 클래스가 자유롭게 사용할 수 있게 함
    • protected 접근 제한자 : 같은 패키지 또는 자식 클래스에서 사용할 수 있게 함
    • private 접근 제한자 : 외부에서 사용될 수 없게 함
    • default 접근 제한자 : 위의 세 접근 제한자 미적용 시 적용, 같은 패키지에 소속된 클래스에서만 사용 가능
  • public > protected > default > private 순으로 접근 제한 강화

(4) 클래스의 접근 제한

1) public 접근 제한

  • 'public' class 클래스명
  • 같은 패키지 뿐만이 아니라 다른 패키지에서도 아무런 제한 없이 사용 가능
  • 라이브러리 클래스 모두 public 접근 제한 보유

2) default 접근 제한

  • class 클래스명 (public 생략)
  • 같은 패키지에서는 아무런 제한 없이 사용 가능하나 다른 패키지에서는 사용 불가

(5) 생성자의 접근 제한

  • 객체 생성을 위해 new 연산자로 생성자를 호출하는데 어디에서나 호출할 수 있는 것은 아님
  • 생성자가 어떤 접근 제한을 갖느냐에 따라 호출 가능 여부 결정

1) public 접근 제한

  • 'public' 생성자명()
  • 모든 패키지에서 아무런 제한 없이 생성자 호출 가능

2) default 접근 제한

  • 생성자명() (접근 제한자 생략)
  • 같은 패키지에서는 아무런 제한 없이 호출 가능하나 다른 패키지에서는 호출 불가

3) protected 접근 제한

  • 'protected' 생성자명()
  • 같은 패키지에 속하는 클래스, 그리고 다른 패키지에 속한 클래스가 자식 클래스일 경우 호출 가능

4) private 접근 제한

  • 'private' 생성자명()
  • 동일 패키지 / 다른 패키지 상관 없이 외부에서 생성자 호출 불가, 오로지 클래스 내부에서만 호출 가능
  • 싱글톤 패턴에서 활용

(6) 필드와 메소드의 접근 제한

  • 필드와 메소드 선언 시 클래스 내부에서만 사용할지, 패키지 내부에서, 다른 패키지에서도 사용 가능할 지 결정 필요
  • 필드와 메소드의 접근 제한에 따라 결정

1) public 접근 제한

  • public 타입 필드;
  • public 리턴 타입 메소드(...) {...}
  • 모든 패키지에서 아무런 제한 없이 필드와 메소드 사용 가능

2) default 접근 제한

  • 타입 필드;
  • 리턴 타입 메소드(...) {...}
  • 같은 패키지에서는 아무런 제한 없이 사용 가능하나 다른 패키지에서는 사용 불가

3) protected 접근 제한

  • protected 타입 필드;
  • protected 리턴 타입 메소드(...) {...}
  • 같은 패키지에 속하는 클래스, 그리고 다른 패키지에 속한 클래스가 자식 클래스일 경우 사용 가능

4) private 접근 제한

  • private 타입 필드;
  • private 리턴 타입 메소드(...) {...}
  • 동일 패키지 / 다른 패키지 상관 없이 외부에서 생성자 호출 불가, 오로지 클래스 내부에서만 사용 가능

(7) 접근 제한 정리

  • 티스토리 오류 수정 후 다시 넣는다

(8) Getter와 Setter 메소드

  • 일반적으로 객체 지향 프로그래밍에서는 객체의 필드를 객체 외부에서 직접적으로 접근하는 것을 막음
  • 외부에서 마음대로 접근하여 값을 변경할 경우 객체의 무결성이 깨질 수 있음
  • 객체 지향 프로그래밍에서는 메소드를 통해 필드를 변경하는 방법을 선호
  • 필드는 외부에서 접근할 수 없도록 막고 메소드는 공개하여 외부에서 메소드를 통해 필드에 접근하도록 유도
  • Setter : 매개값을 검증해서 유요한 값만 객체의 필드로 저장하게 하는 메소드
  • 예시 : 속도를 검증하는 Setter
void setSpeed(double speed) {
	if(speed <0) {
  		this.speed = 0;
  		return; // 속도는 음수가 되어서 안되므로 음수인 경우 0으로 저장하고 실행 종료
 	} else {
  		this.speed = speed;
 	}
}
  • 외부에서 객체의 데이터를 읽을 때도 메소드를 사용하는 것이 좋음. 필드값을 직접 사용할 경우 부적절할 가능성 있음
  • Getter : 메소드로 필드값을 가공한 후 외부로 전달하는 메소드
  • 예시 : 필드값을 외부로 전달할 때 단위를 변경
double getSpeed() {
	double km = speed * 1.6; // 필드값 마일을 km 단위로 환산 후 외부로 리턴
 	return km;
}
  • 클래스 선언 시 필드를 private로 선언해서 외부로부터 보호, 필드에 대한 Setter와 Getter 메소드를 작성하여 필드값을 안전하게 변경/사용하는 것이 좋음
  • 예시
private 타입 fieldName;

// Getter
public 리턴 타입 getFieldName() {
	return fieldName;
}

// Setter
public void setFieldName(타입 fieldName) {
	this.fieldName = fieldName;
}

// ※ 필드 타입이 boolean 일 경우엔 Getter는 get으로 시작하지 않고 is로 시작하는 것이 관례
private boolean stop;

public boolean isStop() {
	return stop;
}
profile
공부했던 내용들을 모아둔 창고입니다.

0개의 댓글