오버 라이딩이란

조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버 라이딩이라고 한다.

(override의 의미는 ~위에 덮어쓰다(overwrite)이다.)

 

class Point {
	int x;
	int y;

	String getLocation() {
		return "x :" + x + ", y : "+y;
	}
}
class Point3D extends Point {
	int z;

	String getLocation() {	// 오버라이딩
	return "x :" + x + ", y : "+ y + ", z :" +z;
	}
}

Point클래스의 getLocation은 한 점의 x, y좌표를 문자열로 반환하도록 작성되었다.

Point클래스는 3차원 좌표계의 한 점을 표현하기 위한 것이므로 조상인 Point클래스로부터 상속받은 getLocation()은 Point 3D클래스에 맞지 않는다.

그래서 이 메서드를 Point3D클래스 자신에 맞게 z축의 좌표값도 포함하여 반환하도록 오버 라이딩하였다.

 

 

 

 

오버 라이딩의 조건

오버 라이딩은 메서드의 내용만을 새로 작성하는 것이므로 메서드는 조상의 것과 완전히 일치해야 한다.

- 이름이 같아야 한다.
- 매개변수가 같아야 한다.
- 반환타입이 같아야 한다.

한마디로 선언부가 서로 일치해야 한다는 것이다. 다만 접근 제어자(access modifier)와 예외(exception)는 제한된 조건 하에서만 다르게 변경할 수 있다.

 

1. 접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.

만일 조상 클래스에 정의된 메서드의 접근 제어자가 protected라면, 이를 오버 라이딩하는 자손 클래스의 메서드는 접근 제어자가 protected나 public이어야 한다. 대부분의 경우 같은 범위의 접근 제어자를 사용한다.

Public > protected > (default), privvate이다.

 

2. 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.

아래의 코드를 보면 child클래스의 parentMethod()에 선언된 예외의 개수가 조장인 Parent클래스의 parentMethod()에 선언된 예외의 개수보다 적으므로 바르게 오버 라이딩되었다.

class Parent {
	void parentMethod() throws IOException, SQLException {
		...
	}
}
class Child extends Parent {
	void parentMethod() throws IOException {
		...
	}	
	...
}

여기서 한 가지 주의할 것이 있는데 예외 문의 개수만 문제가 아니라 조상 클래스 여부도 따져야 한다.

조상 클래스의 메서드를 자손 클래스에서 오버라이딩할 때
1. 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
2. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.
3. 인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.

 

오버 로딩 vs 오버 라이딩

 

오버 로딩과 오버 라이딩은 혼동하기 쉽지만 차이가 명백하다.

오버 로딩은 기존에 없는 새로운 메서드를 추가하는 것이고, 오버 라이딩은 조상으로부터 상속받은 메서드의 내용을 변경하는 것이다.

오버로딩(overloading) 기존에 없는 새로운 메서드를 정의하는 것(new)
오버라이딩(overriding) 상속받은 메서드의 내용을 변경하는 것(change, modify)

아래의 코드를 보고 오버 로딩과 오버 라이딩을 구별할 수 있어야 한다.

class Parent {
		void parentMethod() {}
}

class Child extends Parent {
		void parentMethod() {}
		void parentMethod(int i) {}

		void childMethod() {}
		void childMethod(int i) {}
		void childMethod() {}
}

 

프로그래밍은 어떤 메서드가 어떤 인스턴스로 호출되는가를 실행할 때 알게 되고 컴파일 때는 모른다.

 

 

super

super는 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는 데 사용되는 참조 변수이다.

멤버 변수와 지역변수의 이름이 같을 때 this를 사용할 수 있다. 그래도 조상 클래스의 멤버와 자손 클래스의 멤버가 중복 정의되어 서로 구별해야 하는 경우에만 super를 사용하는 것이 좋다.

조상의 멤버와 자신의 멤버를 구별하는 데 사용된다는 점을 제외하고는 super와 this는 근본적으로 같다.

모든 인스턴스 메서드에는 자신이 속한 인스턴스의 주소가 지역변수로 저장되는데, 이것이 참조 변수인 this와 super의 값이 같다.

 static메서드(클래스 메서드)는 인스턴스와 관련이 없다. 그래서 this와 마찬가지로 super 역시 static메서드에서는 사용할 수 없고 인스턴스 메서드에서만 사용할 수 있다.

 

 

super() - 조상 클래스의 생성자()

this()와 마찬가지로 super() 역시 생성자이다. this()는 같은 클래스의 다른 생성자를 호출하는 데 사용되지만, super()는 조상 클래스의 생성자를 호출하는 데 사용된다.

자손 클래스의 인스턴스를 생성하면, 자손의 멤버와 조상의 멤버가 모두 합쳐진 하나의 인스턴스가 생성된다.

이때 조상 클래스 멤버의 초기화 작업이 수행되어야 하기 때문에 자손 클래스의 생성자에서 조상 클래스의 생성자가 호출되어야 한다.

생성자의 첫 줄에서 조상 클래스의 생성자를 호출해야 하는 이유는 자손 클래스의 멤버가 조상 클래스의 멤버를 사용할 수도 있으므로 조상의 멤버들이 먼저 초기화되어 있어야 하기 때문이다.

이와 같은 조상 클래스 생성자의 호출은 클래스의 상속관계를 거슬러 올라가면서 계속 반복된다. 마지막으로 모든 클래스의 최고 조상인 Object클래스의 생성자인 Object()까지 가서야 끝이 난다.

그래서 Object클래스를 제외한 모든 클래스의 생성자는 첫 줄에 반드시 자신의 다른 생성자 또는 조상의 생성자를 호출해야 한다. 그렇지 않으면 컴파일러는 생성자의 첫 줄에 super();를 자동적으로 추가할 것이다.

Object클래스를 제외한 모든 클래스의 생성자 첫 줄에 생성자.this() 또는 super(),를 호출해야 한다.
그렇지 않으면 컴파일러가 자동적으로 super();를 생성자의 첫 줄에 삽입한다.

인스턴스를 생성할 때는 클래스를 선택하는 것만큼 생성자를 선택하는 것도 중요하다.

1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?
2. 생성자 - 선택한 클래스의 어떤 생성자를 이용해서 인스턴스를 생성할 것인가?

 

6-21 Tv클래스를 주어진 로직대로 완성하시오 완성한 후에 실행해서 주어진 실행결과와 일치하는지 확인하라.

class MyTv {
	boolean isPowerOn;
	int channel;
	int volume;
	final int MAX_VOLUME = 100;
	final int MIN_VOLUME = 0;
	final int MAX_CHANNEL = 100;
	final int MIN_CHANNEL = 1;

	void turnOnOff() {
		// (!) isPowerOn의 값이 true면 false로, false면 true로 바꾼다
	}

	void volumeUp() {
		// (2) voulum의 값이 MAX_VOLUME보다 작을 때만 값을 1증가시킨다/
	}

	void volumeDown() {
		// (3) volum의 값이 MIN_VOLUME보다 클 때만 값을 1감소시킨다.
	}

	void channelUp() {
		// (4) channel의 값을 1 증가시킨다.
		// 만일 channel이 MAX_CHANNEL이면, channel의 값을 MIN_CHANNEL로 다시 바꾼다.
	}

	void channelDown() {
		// (5)channel의 값을 1감소시킨다.
		// 만일 channel이 MIN_CHANNEL이면, channel의 값을 MAX_CHANNEL로 바꾼다.

	} // class MyTv

class Exercise6_21 {
	public static void main(String args[]) {
		MyTv t = new MyTv();
		t.channel = 100;
		t.volume = 0;
		System.out.println("CH:" + t.channel + ", VOL:" + t.volume);

		t.channelDown();
		t.volumeDown();
		System.out.println("CH:" + t.channel + ", VOL:" + t.volume);

		t.volume = 100;
		t.channelUp();
		t.volumeUp();
		System.out.println("CH:" + t.channel + ", VOL:" + t.volume);

	}
}
class MyTv {
	boolean isPowerOn;
	int channel;
	int volume;
	final int MAX_VOLUME = 100;
	final int MIN_VOLUME = 0;
	final int MAX_CHANNEL = 100;
	final int MIN_CHANNEL = 1;

	void turnOnOff() {
		// (!) isPowerOn의 값이 true면 false로, false면 true로 바꾼다
		isPowerOn = !isPowerOn;
	}

	void volumeUp() {
		// (2) voulume의 값이 MAX_VOLUME보다 작을 때만 값을 1증가시킨다/
		if (volume < MAX_VOLUME)
			volume += 1;
	}

	void volumeDown() {
		// (3) volume의 값이 MIN_VOLUME보다 클 때만 값을 1감소시킨다.
		if (volume > MIN_VOLUME)
			volume -= 1;
	}

	void channelUp() {
		// (4) channel의 값을 1 증가시킨다.
		// 만일 channel이 MAX_CHANNEL이면, channel의 값을 MIN_CHANNEL로 다시 바꾼다.
		if (channel == MAX_CHANNEL)
			channel = MIN_CHANNEL;
		else
			channel += 1;
	}

	void channelDown() {
		// (5)channel의 값을 1감소시킨다.
		// 만일 channel이 MIN_CHANNEL이면, channel의 값을 MAX_CHANNEL로 바꾼다.
		if (channel == MIN_CHANNEL)
			channel = MAX_CHANNEL;
		else
			channel -= 1;
	}
} // class MyTv

class Exercise6_21 {
	public static void main(String args[]) {
		MyTv t = new MyTv();
		t.channel = 100;
		t.volume = 0;
		System.out.println("CH:" + t.channel + ", VOL:" + t.volume);

		t.channelDown();
		t.volumeDown();
		System.out.println("CH:" + t.channel + ", VOL:" + t.volume);

		t.volume = 100;
		t.channelUp();
		t.volumeUp();
		System.out.println("CH:" + t.channel + ", VOL:" + t.volume);

	}
}
CH:100, VOL:0
CH:99, VOL:0
CH:100, VOL:100

 

 

 

 

6-22 다음과 같이 정의된 메서드를 작성하고 테스트하시오.

매서드명 : isNumber

기     능 : 주어진 문자열이 모두 숫자로만 이루어져있는지 확인한다.

모두 숫자로만 이루어져 있으면 true를 반환하고, 그렇지 않으면 false를 반환한다.

만일 주어진 문자열이 null이거나 빈문자열 ""이라면 false를 반환한다.

반환타입 : boolean

매개변수 : String str - 검사할 문자열

[Hint] String클래스의 charAt(int i)메서드를 사용하면 문자열의 i번째 위치한 문자를 얻을 수 있다.

public class Exercise6_22 {
	/*
	 * (1) isNumber . 메서드를 작성하시오
	 */
	public static void main(String[] args) {
	String str = "123";
	System.out.println(str+"는 숫자입니까? "+isNumber(str));
	str = "1234o";
	System.out.println(str+"는 숫자입니까? "+isNumber(str));
	}
}
public class Exercise6_22 {

	public static boolean isNumber(String str) {
		if (str == null || str == "") {
			return false;
		}
		for(int i=0; i<str.length();i++) {
			char ch = str.charAt(i);
			
			if (ch<'0' && ch>'9'){
				return false;
			}
		}
		return true;
	}
	public static void main(String[] args) {
		String str = "123";
		System.out.println(str + "는 숫자입니까? " + isNumber(str));
		str = "1234o";
		System.out.println(str + "는 숫자입니까? " + isNumber(str));
	}
}

 

 

 

 

6-23 다음과 같이 정의된 메서드를 작성하고 테스트하시오.

매서드명 : max

기     능 : 주어진 int형 배열의 값 중에서 제일 큰 값을 반환한다. 

만일 주어진 배열이 null이거나 크기가 0인 경우,  -999999를 반환한다.

반환타입 : int 

매개변수 : int[] arr - 최대값을 구할 배열

public class Exercise6_23 {
	/*
	 * (1) max메서드를 작성하시오.
	 */
			}

		}
		return max;
	}

	public static void main(String[] args) {
		int[] data = { 3, 2, 9, 4, 7 };
		System.out.println(java.util.Arrays.toString(data));
		System.out.println("최대값 :" + max(data));
		System.out.println("최대값 :" + max(null));
		System.out.println("최대값 :" + max(new int[] {})); // 0 최대값 크기가 인 배열
	}
}
public class Exercise6_23 {
	/*
	 * (1) max메서드를 작성하시오.
	 */
	static int max(int[] arr) {
		if (arr == null || arr.length == 0)
			return -999999;
		int max = arr[0];

		for (int i = 0; i < arr.length-1; i++) {
			if (arr[i + 1] > max) {
				max = arr[i + 1];
			}

		}
		return max;
	}

	public static void main(String[] args) {
		int[] data = { 3, 2, 9, 4, 7 };
		System.out.println(java.util.Arrays.toString(data));
		System.out.println("최대값 :" + max(data));
		System.out.println("최대값 :" + max(null));
		System.out.println("최대값 :" + max(new int[] {})); // 0 최대값 크기가 인 배열
	}
}
[3, 2, 9, 4, 7]
최대값 :9
최대값 :-999999
최대값 :-999999

 

 

 

6-24 다음과 같이 정의된 메서드를 작성하고 테스트하시오.

 

메서드명 : abs

기     능 : 주어진 값의 절대값을 반환한다.

반환타입 : int

매개변수 : int value

class Exercise6_24 {
		/*
		 * 	(1) abs메서드를 작성하시오.
		 */
	public static void main(String[] args)
	{
		int value = 5;
		System.out.println(value+"의 절대값:"+abs(value));
		value = -10;
		System.out.println(value+"의 절대값:"+abs(value));
	}
}
class Exercise6_24 {
	public static int abs(int value) {
		if (value > 0) {
			return value;
		} else {
			return -value;
		}
	}
	public static void main(String[] args) {
		int value = 5;
		System.out.println(value + "의 절대값:" + abs(value));
		value = -10;
		System.out.println(value + "의 절대값:" + abs(value));
	}
}
5의 절대값:5
-10의 절대값:10

6-11 다음 중 this에 대한 설명으로 맞지 않은 것은? (모두 고르시오)  b

a. 객체 자신을 가리키는 참조 변수이다.

b. 클래스 내에서라면 어디서든 사용할 수 있다.

c. 지역변수와 인스턴스 변수를 구별할 때 사용한다.

d. 클래스 메서드 내에서는 사용할 수 없다.

 

b. 인스턴스 메서드에서만 사용 가능하다.

 

 

6-12 다음 중 오버 로딩이 성립하기 위한 조건이 아닌 것은? (모두 고르시오) c, d

a. 매머드의 이름이 같아야 한다.

b. 매개변수의 개수나 타입이 달라야 한다.

c. 리턴 타입이 달라야 한다.

d. 매개변수의 이름이 달라야 한다

 

c. 리턴타입이 달라야 한다.

리턴 타입은 오버 로딩에 영향을 주지 못한다.

매개변수는 같고 리턴 타입이 다른 경우는 오버 로딩이 성립되지 않는다. 어떤 것을 호출해야 할지 모르기 때문이다.

 

d 매개변수의 이름이 달라야 한다

리턴 타입은 오버 로딩에 영향을 주지 못한다.

 

 

6-13 다음 중 아래의 add메서드를 올바르게 오버 로딩한 것은? (모두 고르시오) b, c, d

long add(int a, int b) { return a+b;}

a. long add(int x, int y) { return x+y;}

b. long add(long a, long b) { return a+b;}

c. int add(byte a, byte b) { return a+b;}

d. int add(long a, int b) { return (int)(a+b);}

 

b, c, d는 모두 메서드의 이름이 add로 같고 매개변수의 타입이 다르다.

 

 

6-14 다음 중 초기화에 대한 설명으로 옳지 않은 것은? ( 모두 고르시오 ) c, e

a. 멤버 변수는 자동 초기화되므로 초기화하지 않고도 값을 참조할 수 있다.

b. 지역변수는 사용하기 전에 반드시 초기화해야 한다.

c. 초기화 블록보다 생성자가 먼저 수행된다. 

d. 명시적 초기화를 제일 우선적으로 고려해야 한다.

e. 클래스 변수보다 인스턴스 변수가 먼저 초기화된다

 

c. 초기화 블록이 먼저 수행된다.

e. 클래스 변수가 먼저 초기화된다.

 

 

6-15 다음 중 인스턴스 변수의 초기화 순서가 올바른 것은? a

a. 기본값-명시적 초기화-초기화 블록-생성자

b. 기본값-명시적 초기화-생성자-초기화 블록

c. 기본값-초기화 블록-명시적 초기화-생성자

d. 기본값-초기화 블록-생성자-명시적 초기화

 

명시적 초기화가 가장 먼저 일어나고 생성자가 늦게 초기화된다.

 

 

6-16 다음 중 지역변수에 대한 설명으로 옳지 않은 것은? (모두 고르시오) a, e

a. 자동 초기화되므로 별도의 초기화가 필요 없다.

b. 지역변수가 선언된 메서드가 종료되면 지역변수도 함께 소멸된다.

c. 매머드의 매개변수로 선언된 변수도 지역변수이다.

d. 클래스 변수나 인스턴스 변수보다 메모리 부담이 적다.

e. 힙(heap) 영역에 생성되며 가비지 컬렉터에 의해 소멸된다.

 

a. 지역변수는 자동 초기화가 되지 않는다.

e 지역변수는 call stack에 생성된다.

 

 

 

6-17 호출 스택이 다음과 같은 상황일 때 옳지 않은 설명은? (모두 고르시오) b

 

a. main 제일 먼저 호출 스택에 저장된 것은 메서드이다.

b. println 메서드를 제외한 나머지 메서드들은 모두 종료된 상태이다. 

c. method2 메서드를 호출한 것은 main 메서드이다.

d. println 메서드가 종료되면 메서드가 method1 수행을 재개한다.

e. main-method2-method1-println. 의 순서로 호출되었다

f. 현재 실행 중인 메서드는 println 뿐이다.

 

b 다른 메서드들은 대기 중인 상태이다.

 

 

6-18 다음의 코드를 컴파일하면 에러가 발생한다 컴파일 에러가 발생하는 라인과 그 이유를 설명하시오.

class MemberCall {
	int iv = 10;
	static int cv = 20;
	int iv2 = cv;
	static int cv2 = iv; // A 라인

	static void staticMethod1() {
		System.out.println(cv);
		System.out.println(iv); // B 라인
	}

	void instanceMethod1() {
		System.out.println(cv);
		System.out.println(iv); // C 라인
	}

	static void staticMethod2() {
		staticMethod1();
		instanceMethod1(); // D 라인
	}

	void instanceMethod2() {
		staticMethod1(); // E 라인
		instanceMethod1();
	}
}

A라인 static변수의 초기화에 인스턴스를 사용할 수 없다.

B라인 static메서드에서는 인스턴스변수를 사용할 수 없다.

D라인 static메서드에서는 인스턴스메서드를 사용할 수 없다.

 

 

 

 

6-19 다음 코드의 실행 결과를 예측하여 적으시오.

public class Exercise6_19 {
	public static void change(String str) {
		str += "456";
	}

	public static void main(String[] args) {
		String str = "ABC123";
		System.out.println(str);
		change(str);
		System.out.println("After change:" + str);
	}
}
ABC123
After change:ABC123

 

 

6-20 다음과 같이 정의된 메서드를 작성하고 테스트하시오.

class Exercise6_20 {

	/*
		(1) shuffle . 메서드를 작성하시오
	*/

	}

	public static void main(String[] args) {
		int[] original = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		System.out.println(java.util.Arrays.toString(original));

		int[] result = shuffle(original);
		System.out.println(java.util.Arrays.toString(result));
	}
}
	public static int[] shuffle(int[] arr) {
		if (arr == null || arr.length == 0) {
			return arr;
		}
		for (int i = 0; i < arr.length - 1; i++) {
			int j = (int) (Math.random() * arr.length);

			int tmp = arr[i];
			arr[i] = arr[j];
			arr[j] = tmp;
		}
		return arr;
	}

	public static void main(String[] args) {
		int[] original = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		System.out.println(java.util.Arrays.toString(original));

		int[] result = shuffle(original);
		System.out.println(java.util.Arrays.toString(result));
	}
}
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[4, 5, 2, 9, 6, 3, 7, 8, 1]

일단 메모리를 공유해야하기 때문에 static으로 int형 shuffle 매서드를 만들고 배열의 자리를 바꾸는 코드를 짜주고 배열을 리턴하면 된다.

6-1 다음과 같은 멤버변수를 갖는 SutdaCard클래스를 정의하시오.

타 입 변수명 설 명
int num 카드의 숫자.(1~10사이의 정수)
boolean isKwang 광이면 true, 아니면 false
class SutdaCard {
	int num;
	boolean isKwang;
}

정의만 한 것이다.

 

6-2 문제 에서 정의한 클래스에 두 개의 생성자와 를 추가해서 실행결과와 같은 결과를 얻도록 하시오.

class Exercise6_2 {
	public static void main(String args[]) {
		SutdaCard card1 = new SutdaCard(3, false);
		SutdaCard card2 = new SutdaCard();
		System.out.println(card1.info()); // 3 . 이 출력된다
		System.out.println(card2.info()); // 1K . 가 출력된다
	}
}

class SutdaCard {
	int num;
	boolean isKwang;

	SutdaCard() {
		this(1, true); // SutdaCard(1, true) . 를 호출한다
	}

	SutdaCard(int num, boolean isKwang) {
		this.num = num;
		this.isKwang = isKwang;
	}

	String info() { // . ( ) K . 숫자를 문자열로 반환한다 광 인 경우 를 덧붙인다 光
		return num + (isKwang ? "K" : "");
	}
}

 

 

6-3 다음과 같은 멤버변수를 갖는  Student클래스를 정의하시오.

타입 변수명 설 명
String name 학생이름
int ban
int no 번호
int kor 국어점수
int eng 영어점수
int math 수학점수
public class Exercise06_3 {
}
class Student {
	String name;
	int ban;
	int no;
	int kor;
	int eng;
	int math;
}

 

 

6-4 문제 에서 정의한 클래스에 다음과 같이 정의된 두 개의 메서드getTotal()과 getAverage()를 추가하시오.

메서드명 : getTotal

기 능 : 국어 영어 수학 의 점수를 모두 더해서 반환한다.

반환타입 : int

매개변수 : 없음

2. : getAverage 메서드명 기 능 총점 국어점수 영어점수 수학점수 을 과목수로 나눈 평균을 구한다 소수점 둘째자리에서 반올림할 것.

반환타입 : float

매개변수 : 없음

class Exercise06_4 {
	public static void main(String args[]) {
		Student s = new Student();
		s.name = "홍길동 ";
		s.ban = 1;
		s.no = 1;
		s.kor = 100;
		s.eng = 60;
		s.math = 76;
		System.out.println("이름 :" + s.name);
		System.out.println("총점 :" + s.getTotal());
		System.out.println("평균 :" + s.getAverage());
	}
}
class Student {
	/*
	 * (1) . 알맞은 코드를 넣어 완성하시오
	 */
}

 

class Exercise06_4 {
	public static void main(String args[]) {
		Student s = new Student();
		s.name = "홍길동 ";
		s.ban = 1;
		s.no = 1;
		s.kor = 100;
		s.eng = 60;
		s.math = 76;
		System.out.println("이름 :" + s.name);
		System.out.println("총점 :" + s.getTotal());
		System.out.println("평균 :" + s.getAverage());
	}
}

class Student {
	String name;
	int ban;
	int no;
	int kor;
	int eng;
	int math;

	int getTotal() {
		return kor+math+eng;
	}
	float getAverage() {
		return (int)(getTotal() / 3f * 10 + 0.5f) / 10f;
	}
}
이름 :홍길동 
총점 :236
평균 :78.7

평균을 구하는식에서 int타입은 소수점이 버려지는것을 이용한 식이다.

 

 

 

6-6 두 점의 거리를 계산하는 를 완성하시오. getDistance()

[Hint] 제곱근 계산은 를 사용하면 된다. Math.sqrt(double a)

public class Exercise6_06 {
	static double getDistance(int x, int y, int x1, int y1) {
		/*
		 * (1) . 알맞은 코드를 넣어 완성하시오
		 */
	}
	public static void main(String args[]) {
		System.out.println(getDistance(1, 1, 2, 2));
	}
}
public class Exercise6_06 {
	static double getDistance(int x, int y, int x1, int y1) {

		return Math.sqrt((x-x1)*(x-x1)+(y-y1)*(y-y1));
	}
	public static void main(String args[]) {
		System.out.println(getDistance(1, 1, 2, 2));
	}
}
1.4142135623730951

제곱근을 구하면 된다.

Math.sqrt는 double타입을 반환한다.

 

 

 

6-7 문제 에서 작성한 클래스메서드getDistance()를 MyPoint클래스의 인스턴스메서드로 정의하시오.

class MyPoint{
	int x;
	int y;

	MyPoint(int x, int y) {
	this.x = x;
	this.y = y;
	}
	/*
	 * (1) getDistance . 인스턴스메서드 를 작성하시오
	 */
}
class Exercise6_07 {
	public static void main(String args[]) {
		MyPoint p = new MyPoint(1, 1);
		// p (2,2) . 와 의 거리를 구한다
		System.out.println(p.getDistance(2, 2));
	}
}
class MyPoint{
	int x;
	int y;

	MyPoint(int x, int y) {
	this.x = x;
	this.y = y;
	}
	double getDistance(int x1, int y1) {
		return Math.sqrt((x-x1)*(x-x1)+(y-y1)*(y-y1));
	}
}
class Exercise6_07 {
	public static void main(String args[]) {
		MyPoint p = new MyPoint(1, 1);
		// p (2,2) . 와 의 거리를 구한다
		System.out.println(p.getDistance(2, 2));
	}
}
1.4142135623730951

 

 

 

6-8 다음의 코드에 정의된 변수들을 종류별로 구분해서 적으시오.

public class PlayingCard {
	int kind;
	int num;
	static int width;
	static int height;

	PlayingCard(int k, int n) {
		kind = k;
		num = n;
	}

	public static void main(String args[]) {
		PlayingCard card = new PlayingCard(1, 1);
	}
}

- 클래스변수(static변수) : width, height

- 인스턴스변수 : kind, num

- 지역변수 : k, n, card, args

변수의 종류 선언위치 생성시기
클래스변수

클래스 영역 클래스가 메모리에 올라갈 때
인스턴스변수 인스턴스가 생성되었을 때
지역변수 클래스 영역 이외의 영역
(메서드, 생성자, 초기화 블럭 내부)
변수 선언문이수행되었을 때

 

6-9 다음은 컴퓨터 게임의 병사 를 클래스로 정의한 것이다 이 클래스의 멤버중에 static을 붙여야 하는 것은 어떤 것들이고 그 이유는 무엇인가?

(단, 모든 병사의 공격력과 방어력은 같아야 한다.)

class Marine {
	int x = 0, y = 0; // Marine (x,y) 의 위치좌표
	int hp = 60; // 현재 체력
	int weapon = 6; // 공격력
	int armor = 0; // 방어력

	void weaponUp() {
		weapon++;
	}
	void armorUp() {
		armor++;
	}
	void move(int x, int y) {
		this.x = x;
		this.y = y;
	}
}
class Marine {
	int x = 0, y = 0; // Marine (x,y) 의 위치좌표
	int hp = 60; // 현재 체력
	static int weapon = 6; // 공격력
	static int armor = 0; // 방어력

	static void weaponUp() {
		weapon++;
	}
	static void armorUp() {
		armor++;
	}
	void move(int x, int y) {
		this.x = x;
		this.y = y;
	}
}

공통적인 값을 가지는 변수들을 static변수로 선언해야한다.

공격력과 방어력이 같기 위해서는 기본 공격 방어력과 증감연산자가 들어간 메서드에 static을 넣으면 된다.

 

 

[6-10] 다음 중 생성자에 대한 설명으로 옳지 않은 것은 모두 고르시오 ? ( b,e )

a. 모든 생성자의 이름은 클래스의 이름과 동일해야한다.

b. 생성자는 객체를 생성하기 위한 것이다.

c. 클래스에는 생성자가 반드시 하나 이상 있어야 한다.

d. 생성자가 없는 클래스는 컴파일러가 기본 생성자를 추가한다.

e. 생성자는 오버로딩 할 수 없다.

 

b 객체를 생성하는것은 new 연산자이고 생성자는 초기화할 목적으로 사용된다.

e 생성자는 오버로딩이 가능하다.

 

추가적으로 생성자는 리턴값이 없다. 

생성자도 메서드이기 때문에 리턴값이 없다는 의미인 void를 적어야 하지만, 모든 생성자가 리턴값이 없으므로 생략이 가능하다.

변수를 선언하고 처음으로 값을 저장하는 것을 변수의 초기화라고 한다. 변수의 초기화는 경우에 따라서 필수적이기도 하고 선택적이기도 하지만, 가능하면 선언과 동시에 적절한 값으로 초기화 하는것이 바람직하다.

멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어지므로 초기화하지 않고 사용해도 되지만, 지역변수는 사용하기 전에 반드시 초기화해야 한다.

class InitTest {
	int x;	//인스턴스변수
	int y = x;	//인스턴스변수

void method1() {
		int i;	//지역변수
		int j =i;	//에러 지역변수를 초기화하지 않고 사용
	}
}

x, y는 지역변수이고 i와 j는 지역변수이다 인스턴스 변수x는 초기화를 해주지 않아도 자동적으로 int형의 기본값인 0으로 초기화되므로 요류가 나지 않지만  method1()의 지역변수i는 자동적으로 초기화되지 않으므로, 초기화 되지 않은 상태에서 변수j를 초기화 하는데 사용될 수 없다.

멤버변수(클래스변수와 인스턴스변수)와 배열의 초기화는 선택적이지만, 지역변수의 초기화는 필수적이다.

각 타입의 기본값(default value)는 다음과 같다.

자료형 기본값
boolean false
char '\u0000'
byte, short, int 0
long 0L
float 0.0f
double 0.0d 또는 0.0
참조형변수 null

 

 

변수의 초기화에 대한 예를 몇 가지 더 살펴보자.

선언예 설 명
int i =10;
int j=10;
int형 변수 i를 선언하고 10으로 초기화 된다.
int형 변수 j를 선언하고 10으로 초기화 된다.
int i=10,  j=10; 같은 타입의 변수는 콤마(,)를 사용해서 함께 선언하거나 초기화 할 수 있다.
int i=10;, long j=10; 에러. 타입이 다른 변수는 함께 선언하거나 초기화할 수 없다.
int i=10;
int j=i;
변수 i에 저장된 값으로 변수 j를 초기화 한다.
변수 j는 i의 값인 10으로 초기화 된다.
int j=i;
int i=10;
에러. 변수 i가 선언되기 전에 i를 사용할 수 없다.

 

멤버변수의 초기화 방법
1. 명시적 초기화(explicit initialization)
2. 생성자(constructor)
3. 초기화 블럭(initialization block)
	- 인스턴스 초기화 블럭 :인스턴스변수를 초기화 하는데 사용.
	- 클래스 초기화 블럭 :클래스변수를 초기화 하는데 사용.

 

명시적 초기화(explicit initialization)

변수를 선언과 동시에 초기화하는 것을 명시적 초기화라고 한다.

 

초기화 블럭(initialization block)

초기화 블럭에는 클래스 초기화 블럭과 인스턴스 초기화 블럭 두 가지 종류가 있다.

클래스 초기화 블럭 클래스변수의 복잡한 초기화에 사용된다.
인스턴스 초기화 블럭 인스턴스변수의 복잡한 초기화에 사용된다.

초기화 블럭을 작성하려면, 인스턴스 초기화 블럭은 단순히 클래스 내에 블럭{}만들고 그안에 코드들을 작성하기만 하면 된다. 그리고 클래스 초기화 블럭은 인스턴스 초기화 블럭 앞에 단순히 static을 덧붙이기만 하면 된다.

 

초기화 블럭 내에는 매서드 내에서와 같이 조건문, 반복문, 예외처리구문 등을 자유롭게 사용할 수 있으므로, 초기화 작업이 복잡하여 명시적 초기화만으로는 부족한 경우 초기화 블럭을 사용한다.

class InitBlock {
	static {/* 클래스 초기화블럭 입니다. */}
	{/*인스턴스초기화블럭 입니다. */ }
}

클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때 한번만 수행되며, 인스턴스 초기화블럭은 생성자와 같이 인스턴스를 생성할 때 마다 수행된다.

그리고 생성자보다 인스턴스 초기화 블럭이 먼저 수행된다는 것도 기억해두자.

클래스가 처음 로딩될 때 클래스변수들이 자동적으로 메모리에 만들어지고, 곧바로 클래스 초기화블럭이 클래스변수들을 초기화하게 되는 것이다.

 

인스턴스 변수의 초기화는 주로 생성자를 사용하고, 인스턴스 초기화 블럭은 모든 생성자에서 공통으로 수행돼야 하는 코드를 넣는데 사용한다.

Car() {
	count++;
	serialNo = count;
	color="White";
	gearType ="Auto";
}
Car(String color, String gearType) {
	count++;
	serialNo = count;
	this.color=color;
	this.gearType = gearType;
}

모든 생성자에 공통적으로 수행되어야 하는 문장들이 있을 때, 이 문장들을 각 생성자마다 써주기 보다는 아래와 같이 인스턴스 블럭에 넣어주면 코드가 보다 간결해진다.

{
	count++;
	serialNo = count;
}
Car() {
	clor = "White";
	gearType="Auto";
}

Car(String color, String gearType) {
	this.color=color;
	this.gearType = gearType;
}

코드의 중복을 제거하는 것은 코드의 신뢰성을 높여 주고, 오류의 발생가능성을 줄여 준다는 장점이 있다.

즉, 재사용성을 높이고 중복을 제거하는 것이 바로 객체 지향 프로그래밍이 추구하는 목표이다.

프로그래머는 이와 같은 객체지향언어의 요소들을 잘 이해하고 활용하여 코드의 중복을 최대한 제거해야 한다.

 

public class BlockTest {
	static {
		System.out.println("static { }");
	}
	
	{
		System.out.println("{ }");
	}
	
	public BlockTest() {
		System.out.println("생성자");
	}
	
	public static void main(String args[]) {
		System.out.println("BlockTest bt = new BlockTest(); ");
		BlockTest bt = new BlockTest();
		
		System.out.println("BlockTest bt2 = new BlockTest(); ");
		BlockTest bt2 = new BlockTest();
	}
}

예제가 실행되면서 BlockTest가 메모리에 로딩될 때, 클래스 초기화 블럭이 가장 먼저 수행되어 static {}이 화면에 출력된다. 그 다음에 main메서드가 수행되어 BlockTest인스턴스가 생성되면서 인스턴스 초기화 블럭이 먼저 수행되고, 끝으로 생성자가 수행된다.

클래스 초기화 블럭은 처음 메모리에 로딩될 때 한번만 수행되었지만, 인스턴스 초기화 블럭은 인스턴스가 생성될 때 마다 수행되었다.

public class StaticBlockTest {
	static int[] arr = new int[10];

	static {
		for(int i=0;i<arr.length;i++) {
			// 1과 10사이의 임의이 값을 배열 arr에 저장한다.
			arr[i] = (int)(Math.random()*10) + 1;
		}
	}
	public static void main(String args[]) {
		for(int i=0; i<arr.length;i++)
			System.out.println("arr["+i+"] :" + arr[i]);
	}
}
arr[0] :7
arr[1] :2
arr[2] :8
arr[3] :1
arr[4] :7
arr[5] :8
arr[6] :7
arr[7] :7
arr[8] :5
arr[9] :10

명시적 초기화를 통해 배열 arr을 생성하고, 클래스 초기화 블럭을 이용해서 배열의 각 요소들을 random()을 사용해서 임의의 값으로 채우도록 했다.

이처럼 배열이나 예외처리가 필요한 초기화에서는 명시적 초기화만으로는 복잡한 초기화작업을 할 수 없다.

이런 경우에 추가적으로 클래스 초기화 블럭을 사용하도록 한다.

인스턴스변수의 복잡한 초기화는 생성자 또는 인스턴스 초기화블럭을 사용한다.

 

멤버변수의 초기화 시기와 순서

클래스변수의 초기화시점	클래스가 처음 로딩될 때 단 한번 초기화 된다.
인스턴스변수의 초기화시점	인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.

클래스변수의 초기화순서	기본값 > 명시적초기화 > 클래스초기화블럭
인스턴스변수의 초기화순서	기본값 > 명시적초기화 > 인스턴스 초기화 블럭 > 생성자

 

프로그램 실행도중 클래스에 대한 정보가 요구될 때, 클래스는 메모리에 로딩된다.

하지만, 해당 클래스가 이미 메모리에 로딩되어 있다면, 또다시 로딩하지 않는다. 초기화도 마찬가지로 수행하지 않는다.

클래스의 로딩 시기는 JVM의 종류에 따라 다를 수 있다.

필요할때 로딩하는lazyload와 실행효율을 높이기위해 미리 로딩하는preload가 있다.

 

class InitTest {
	static int cv = 1;
	int iv = 1;
	static { cv = 2; }
		{ iv = 2; }
	InitTest() {
		iv = 3;
	}
}

 

클래스 초기화 인스턴스 초기화
기본값 명시적
초기화
클래스
초기화블럭
기본값 명시적
초기화
인스턴스
초기화블럭
생성자
cv : 0

1

2

cv : 2
iv : 0
cv : 2
iv : 1
cv : 2
iv : 2
cv : 2
iv : 3
1 2 3 4 5 6 7

클래스변수 초기화 (1~3) :  클래스가 처음 메모리에 로딩될 때 차례대로 수행됨.

인스턴스변수 초기화(4~7) : 인스턴스를 생성할 때 차례대로 수행됨

※클래스변수는 항상 인스턴스변수보다 항상 먼저 생성되고 초기화 된다.

메서드의 선언과 구현

int add(int x, int y)
{
	int result = x + y;
    return result;	//작업 결과(반환값)을 반환한다.
}

메서드는 크게 두 부분, 선언부와 구현부로 이루어져 있다.

메서드를 정의한다는 것은 선언부와 구현부를 작성하는 것을 뜻하며 다음과 같은 형식으로 메서드를 정의한다

 

반환타입 메서드 이름 (타입 변수명, 타입 변수명, ...)	//선언부
{
	//메서드 호출시 수행될 코드(구현부)
}
int add(int a, int b)	// 선언부
{
	int result = a + b;	//구현부
    return result;	//구현부
}

 

메서드 선언부(method declaration, method header)

메서드 선언부는 메서드의 이름과 매개변수 선언, 그리고 반환 타입으로 구성되어 있으며, 메서드가 작업을 수행하기 위해 어떤 값들을 필요로 하고 작업의 결과로 어떤 타입의 값을 반환하는지에 대한 정보를 제공한다.

int add(int x, int y) {
	int result = x + y;
    
    return result;	//결과를 반환
}

 

매개변수 선언(parameter declaration)

매개변수는 메서드가 작업을 수행하는데 필요한 값들(입력)을 제공받기 위한 것이며, 필요한 값의 개수만큼 변수를 선언하며 각 변수 간의 구분은 쉼표', '를 사용한다.

일반적인 변수선언과 달리 두 변수의 타입이 같아도 변수의 타입을 생략할 수 없다는 특징이 있다.

매개변수도 메서드 내에서 선언된 것으로 간주되므로 지역변수이다.

 

메서드의 이름(method name)

메서드의 이름도 앞서 배운 변수의 명명규칙대로 작성하면 된다.

메서드의 이름은'add'처럼 동사인 경우가 많으며, 이름만으로도 메서드의 기능을 쉽게 알 수 있도록 함축적이면서도 의미 있는 이름을 짓도록 노력해야 한다.

 

반환 타입(return type)

메서드의 작업 수행 결과인 반환 값의 타입을 적는다 반환 값이 없는 경우 반환 타입으로 void를 적어야 한다.

 

매서드의 구현부(method body, 메서드 몸통)

매서드의 선언부 다음에 오는 괄호{}를 메서드의 구현부라고 하는데, 여기에 메서드를 호출했을 때 수행될 문장들을 넣는다.

 

return문 

메서드의 반환 타입이 void가 아닌 경우, 구현부{} 안에 return 반환 값; 이 반드시 포함되어 있어야 한다. 이 문장은 작업을 수행한 결과인 반환 값을 호출한 메서드로 전달하는데, 이 값의 타입은 반환 타입과 일치하거나 적어도 자동 형 변환이 가능한 것이어야 한다. 여러 개의 변수를 선언할 수 있는 매개변수와 달리 return문은 단 하나의 값만 반환할 수 있는데, 메서드로의 입력(매개변수)은 여러 개일 수 있어도 출력(반환 값)은 최대 하나만 허용하는 것이다.

 

 

지역변수(local variable)

메서드 내에 선언된 변수들은 그 메서드 내에서만 사용할 수 있으므로 서로 다른 메서드라면 같은 이름의 변수를 선언해도 된다. 이처럼 메서드 내에 선언된 변수를 지역변수(local variable)라고 한다.

 

 

메서드의 호출

메서드를 정의했어도 호출되지 않으면 아무 일도 일어나지 않는다. 메서드를 호출해야만 구현부{}의 문장들이 수행된다.

그러나 main메서드는 프로그램 실행 시 OS에 의해 자동적으로 호출된다.

또한 메서드를 호출하는 방법은 다음과 같다.

메서드이름(값1,값2,...): // 메서드를 호출하는 방법

print99danAll();	// void print99danAll()을 호출
int result = add(3, 5);	// int add(int x, int y)를 호출하고, 결과를 result에 저장

 

 

인자(argument)와 매개변수(parameter)

메서드를 호출할 때 괄호() 안에 지정해준 값들을 인자(argument)또는 인수라고 하는데, 인자의 개수와 순서는 호출된 메서드에 선언된 매개변수와 일치해야 한다. 그리고 인자는 매서드가 호출되면서 매개변수에 대입되므로 인자의 타입은 매개변수의 타입과 일치하거나 자동 형 변환이 가능한 것이어야 한다.

 

메서드의 실행 흐름

같은 클래스 내의 메서드끼리는 참조 변수를 사용하지 않고도 서로 호출이 가능하지만 static메서드는 같은 클래스 내의 인스턴스 메서드를 호출할 수 없다.

public class MyMathTest {
	public static void main(String[] args) {
		MyMath mm = new MyMath();
		
		long result1 = mm.add(5L, 3L);
		long result2 = mm.subtract(5L, 3L);
		long result3 = mm.multiply(5L, 3L);
		double result4 = mm.divide(5L, 3L);
		
		System.out.println("add(5L, 3L) = " + result1);
		System.out.println("subtract(5L, 3L) = " + result2);
		System.out.println("multiply(5L, 3L) = " + result3);
		System.out.println("divide(5L, 3L) = " + result4);
	}

}

class MyMath {long add(long a, long b) {
	long result = a+b;
	return result;
	}

	long subtract(long a, long b) {return a -b;}
	long multiply(long a, long b) { return a * b;}
	double divide(double a, double b) {return a / b;}
}
add(5L, 3L) = 8
subtract(5L, 3L) = 2
multiply(5L, 3L) = 15
divide(5L, 3L) = 1.6666666666666667

MyMath클래스를 이용한 예제이다. divide에 선언된 매개변수 타입은 double형인데 이와 다른 long형의 값인 5L과 3L을 사용해서 호출하는 것이 가능하다.

 

return문

return문은 현재 실행 중인 메서드를 종료하고 호출한 메서드로 돌아간다.

반환 값의 유무에 관계없이 모든 메서드에 넌 적어도 하나의 return문이 있어야 한다.

반환 값이 void인 경우 return문이 없어도 문제가 없던 이유는 컴파일러가 메서드의 마지막에 return을 자동적으로 추가하기 때문이다.

그러나 반환 타입이 void가 아니 경우, 반드시 return문이 있어야 한다.

 

매개변수의 유효성 검사

메서드를 작성하는 사람은 호출하는 쪽에서 알아서 적절한 값을 넘겨준다는 보장이 없으므로 가능ㅎ나 모든 경우의 수에 대해 고민하고 그에 대비한 코드를 작성해야 한다.

 

 

+ Recent posts