이것이 자바다 의 Chapter06 을 참고하여 정리한 포스트입니다.

클래스 정리 - 1

Object 객체
속성을 가지고 있고 다른 것과 식별 가능한 것
객체의 속성과 동작들을 각각 field와 method 라고 부른다

객체지향 프로그래밍 특징

  • 캡슐화 (Encapsulation)
    객체의 필드, 메소드 하나로 묶고, 실제 구현 내용을 감추는 것을 말한다.
    자바 언어는 캡슐화된 멤버를 노출시킬 것인지, 숨길 것인지를 결정하기 위해 접근 제한자 (Access Modifier)를 사용한다.
    접근 제한자는 객체의 필드와 메소드의 사용범위를 제한하여 외부로부터 보호한다.

  • 상속 (Inheritance)
    상위 객체는 자기가 가지고 있는 field 와 method 를 하위 객체에 물려주어 하위 객체가 사용할 수 있게 해준다.
    상위 객체를 재사용해서 하위 객체를 쉽고 빠르게 설계하도록 도와주고, 반복코드의 중복을 줄여준다.

  • 다형성 (Polumorphism)
    같은 타입이지만 실행 결과가 다양한 객체를 이용할 수 있는 성질이다.
    (코드 측면) 하나의 타입에 여러 객체를 대입함으로써 다양한 기능을 이용할 수 있도록 하며, 자바는 다형성을 위해 부모 클래스 또는 인터페이스 타입 변환을 허용한다.
    부모 타입에는 모든 자식 객체가 대입될 수 있고, 인터페이스 타입에는 모든 구현 객체가 대입될 수 있다.
    ex) 자동차 설계 시 타이어 인터페이스 타입을 적용했다면 이 인터페이스를 구현한 실제 타이어들은 어떤 것이든 상관없이 장착(대입)이 가능하다.

객체와 클래스

객체를 생성하는 순서

클래스(설계도) → 인스턴스(객체)

메모리에서 사용하고 싶은 객체가 있다면 설계도로 해당 객체를 만드는 작업 필요하다.
클래스에 객체를 생성하기 위한 필드와 메소드가 정의되어 있다.

객체지향 프로그래밍 개발 세 가지 단계

  1. 클래스를 설계해야 한다
  2. 설계된 클래스를 가지고 사용할 객체를 생성해야 한다
  3. 생성된 객체를 이용한다.
  • 클래스 선언
1
2
public class 클래스 이름 {
}

클래스 선언에 public 접근 제한자를 붙이면 컴파일 에러가 발생한다.
가급적이면 소스파일 하나당 동일한 이름의 클래스 하나를 선언하는 것이 좋다.

  • 객체 생성과 클래스 변수
    new 는 클래스로부터 객체를 생성시키는 연산자
    new 연산자로 생성된 객체를 메모리 힙(heap) 영역에 생성
1
2
	new 클래스();

메모리 내에서 생성된 객체의 위치를 모르면 객체를 사용할 수 없다.
그래서 new 연산자는 힙 영역에 객체를 생성시킨 후, 객체의 주소를 리턴하도록 되어 있다.
이 주소를 참조 타입인 클래스 변수에 저장해 두면, 변수를 통해 객체를 사용할 수 있다.

다음은 클래스 타입으로 선언된 변수에 new 연산자가 리턴한 객체의 주소를 저장하는 코드이다.

1
2
클래스 변수;
변수 = new 클래스();

클래스 변수 선언과 객체 생성을 한 개의 실행문으로 작성 가능하다.

1
클래스 변수 = new 클래스();

클래스를 선언하고 클래스로부터 객체 생성하기


[Student.java] 클래스 선언

1
2
3
4
	package chap06;	
	public class Student {

	}

[Student_Example.java] 클래스로부터 객체 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
	package chap06;	

	// new 는 클래스로부터 객체를 생성시키는 연산자
	// new 연산자로 생성된 객체는 메모리 heap 영역에 생성

	public class StudentExample {
		public static void main(String[] args) {
			Student s1 = new Student();
			System.out.println("s1 변수가 Student 객체를 참조합니다.");

			Student s2 = new Student();
			System.out.println("s2 변수가 또 다른 Student 객체를 참조합니다.");
		}
	}
	// new 연산자를 사용한 만큼 객체가 메모리에 생성된다.
	// 이러한 객체들은 Student 클래스의 인스턴스 들이다.

new 연산자를 사용하여 메모리에 클래스 변수와 객체가 생성된다.

클래스의 두 가지 용도

  1. 라이브러리 (API : Application Program Interface) 용
  2. 실행 용
    라이브러리 클래스는 다른 클래스에서 이용할 목적으로 설계
    실행 클래스는 프로그램의 실행 진입점인 main() 메소드를 제공하는 역할
    Student 는 라이브러리 클래스, studentExample 은 실행 클래스
    Student에 main() 메소드를 작성해서 라이브러리인 동시에 실행 클래스로 만들 수 있다.
1
2
3
4
5
6
7
8
public class Student {    //라이브러리로서의 코드 (필드, 생성자, 메소드)

	//실행하기 위한 코드
	public static void main(String[] args) {
	student s1 = new Student();
	System.out.println("s1 변수가 Student 객체를 참조합니다.");
	}
}

대부분의 객체 지향 프로그램은 라이브러리와 실행클래스가 분리 되어 있다.
가급적이면 각 예제에서 두 클래스를 분리해서 작성해야 한다.

클래스의 구성 멤버 [field, constructor, method]

  1. 필드 (Field)
    1
    
    int fieldName; // 필드 : 객체의 데이터가 저장되는 곳 
    
    • 변수라고 부르지는 않는다.
    • 변수 : 생성자와 메소드 내에서만 사용되고 생성자와 메소드가 종료되면 자동 소멸
    • 필드 : 생성자와 메소드 전체에서 사용되며 객체가 소멸되지 않는 한 객체와 존재
  2. 생성자 (Constructor)
    1
    
     ClassName() {...} // 생성자 : 객체 생성 시 초기화 역할 담당 
    
    • new 연산자로 호출되는 틀별한 중괄호 { } 블록
    • 객체 생성 시 초기화를 담당, 메소드와 비슷하지만 클래스 이름으로 되어있고 리턴 타입이 없다.
  3. 메소드 (Method)
    1
    
     void methodName() {...} // 메소드 : 객체의 동작에 해당하는 실행 블록
    

1. 필드 (Field)

1-1. 필드 사용


클래스 내부의 생성자나 메소드에서 사용할 경우 단순히 필드 이름으로 읽고 변경하면 되지만,
클래스 외부에서 사용할 경우 우선적으로 클래스로부터 객체를 생성한 뒤 필드를 사용해야 한다.
그 이유는 필드는 객체에 소속된 데이터이므로 객체가 존재하지 않으면 필드도 존재하지 않기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Person 클래스
void method() {
	//Car 객체 생성
	Car myCar = new Car();
	// 필드 사용
	myCar.speed = 60;
}

Car 클래스
//필드
int speed;

//생성자
Car() {
	speed = 0;
}

//메소드 
void method(...) {
	speed = 10;
}
  • 위 코드를 보면 Car 클래스의 speed 필드는 생성자와 메소드에서 변경이 가능하다. 사용 방법은 변수와 동일,
    차이점은 변수는 자신이 선언된 생성자 또는 메소드 블록 내부에만 사용할 수 있는 반면 필드는 생성자와 모든 메소드에서 사용이 가능하다.
    외부 Person 클래스에서 Car 클래스의 speed 필드값을 사용하려면 Car 객체를 우선 생성해야 한다.
    Car myCar = new Car();

2. 생성자 Constructor

new 연산자와 같이 사용되어 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당한다.

  • 객체 초기화
    필드를 초기화하거나, 메소드를 호출해서 객체를 사용할 준비를 한다.
    생성자를 실행시키지 않고는 클래스로부터 객체를 만들 수 없다.
    new 연산자에 의해 생성자가 성공적으로 실행되면 힙(heap)영역에 객체가 생성되고 객체의 주소가 리턴된다.
    리턴된 객체의 주소는 클래스 타입 변수에 저장되어 객체 접근 시 이용한다.
    만약 생성자가 성공적으로 실행되지 않고 예외(에러)가 발생했다면 객체를 생성되지 않는다.

2-1 기본 생성자

1
	[public] 클래스() { }

클래스가 public class 로 선언되면 기본 생성자에도 public이 붙지만,
클래스가 public 없이 class 로만 선언되면 기본 생성자에도 public가 붙지 않는다.

ex) 소스파일 Car.java

1
	public class Car { }

바이트 코드 파일 Car.class

1
2
3
		public class Car {
			public Car() { } // 자동 추가, Car() <- 기본 생성자
		}

그렇기 때문에 클래스에 생성자를 선언하지 않아도 new 연산자 뒤에 기본 생성자를 호출해 객체를 생성시킬 수 있다. ex) Car myCar = new Car();

2-2 생성자 선언

생성자 블록

1
2
3
	클래스(매개변수 선언,...) {		
	//객체의 초기화 코드 			
	}

메소드와 비슷한 모양을 가지고 있으나, 리턴 타입이 없고 클래스 이름과 동일하다.
매개 변수 선언은 생략할 수도 있고 여러 개를 선언 할 수 있다.
매개 변수는 new 연산자로 생성자를 호출할 때 외부의 값을 생성자 블록 내부로 전달하는 역할을 한다.

ex) Car 생성자를 호출할 때 세 개의 값을 제공한다고 가정

1
2
	Car myCar = new Car("그랜저", "black", 300);
	// 두 매개값은 Srting 타입이고 마지막 매개값은 int 타입인 것을 볼 수 있다. 

생성자 선언

1
2
3
4
5
	public class Car {
	// 생성자
	Car(String model, Srting color, int maxSpeed) {...}
	}
	// 클래스에 생성자가 명시적으로 선언되어 있을 경우에 반드시 선언된 생성자를 호출해서 객체를 생성해야 한다.

2-3 필드 초기화

클래스로부터 객체가 생성될 때 필드는 기본 초기값으로 자동 생성된다.
다른 값으로 선언하고 싶을 때 두 가지 방법

  1. 필드를 선언할 때 초기값을 주는 방법
  2. 생성자에서 초기값을 주는 방법
  • ex) 1. 필드를 선언하며 대한민국으로 초기값 기입
    1
    2
    3
    4
    5
    
    public class Korean {
      String nation = "대한민국"; // 필드 선언하며 "대한민국"으로 초기값 기입
      String name;
      String ssn;
    }
    
    1
    2
    
      Korean K1 = new Korean();
      Korean K2 = new Korean(); // 모두 "대한민국이 저장"
    
  • ex) 2. 객체 생성 시점에서 외부에서 제공되는 다양한 값들로 초기화
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    public class Korean {
      //필드
      String nation = "대한민국";
      String name;
      String ssn;
    
      //생성자
    public Korean(String n, String s) {
      name = n; // name : 필드, n : 매개변수
      ssn = s; // ->this.ssn = s; 으로 변경 
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    
      Korean K1 = new Korean("김자바", "011223-3123456");
      Korean K2 = new Korean("이자바", "991122-1123456");
      /*
     위 코드로 "김자바", "이자바"는 매개 변수 n을 통해 전달
     주민번호는 `매개변수 s`를 통해 전달
     이 값들은 name 필드와 ssn 필드의 `초기값`으로 사용됨
      */
    

2-4 생성자 오버로딩 (Constructor Overloading)

다양한 방법으로 객체를 생성할 수 있도록 생성자 Overloading 을 제공해준다.
매개 변수를 달리하는 생성자를 여러 개 선언하는 것을 말한다.

1
2
3
4
5
6
7
8
	public class 클래스 {
		클래스 ( [타입 매개변수, ...] ) {
		...
		}
		클래스 ( [타입 매개변수, ...] ) {	
		...	 //생성자 오버로딩 : 매개 변수의 타입, 개수, 순서가 다르게 선언
		}
	}
  • ex) Car 클래스에서 생성자 오버로딩
    1
    2
    3
    4
    5
    
    public class Car {
      Car() {...}
      Car(String model) {...}
      Car(String model, String color) {...}
    }
    

주의할 점
매개 변수의 타입과 개수 그리고 선언된 순서가 똑같을 경우 매개 변수 이름만 바꾸는 것을 생성자 오버로딩이라 볼 수 없다.

1
2
	ex) Car(String model, String color) {...}
	    Car(String color, String model) {...} //오버로딩이 아님

생성자가 오버로딩되어 있을 경우, new 연산자로 생성자를 호출할 때 제공되는 매개값의 타입과 수에 의해 호출 생성자 결정한다.

1
2
3
	ex) Car car1 = new Car();
	    Car car2 = new Car("그랜저", "흰색");
	    Car car3 = new Car("그랜저", "흰색", 300);

2-5 다른 생성자 호출 [ this() ]

생성자 오버로딩이 많아질 경우 생성자 간의 중복된 코드가 발생 가능하다.
매개 변수의 수만 달리하고 필드 초기화 내용이 비슷한 생성자에서 이러한 현상 많이 확인된다.
이 경우에는 필드 초기화한 내용은 한 생성자에만 집중적으로 작성하고,
나머지 생성자에는 초기화 내용을 가지고 있는 생성자를 호출하는 방법으로 개선한다.
생성자에서 다른 생성자 호출할 시 this() 코드를 사용한다.

1
2
3
4
	클래스( [메개변수선언, ...] ) {
		this( 매개변수, ..., , ...);	//클래스의 다른 생성자 호출
		실행문
	}
  • ex) 중복코드 확인
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
      Car(String model){
          this.model= model;		//중복 코드
          this.color = "흰색";		//중복 코드
          this.maxSpeed = 250;		//중복 코드
          }
      //생성자 
      Car(String model, String color){
          this.model = model;		//중복 코드
          this.color = color;		//중복 코드
          this.maxSpeed = 250;		//중복 코드
      }
      //생성자 
      Car(String model, String color, int maxSpeed) {
          this.model = model;		//중복 코드
          this.color = color;		//중복 코드
          this.maxSpeed = maxSpeed;		//중복 코드
    
  • 다른 생성자를 호출해서 중복 코드 줄이기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    public class Car {
      //필드
      String company = "현대자동차";
      String model;
      String color;
      int maxSpeed;
    
      //생성자
      Car() {
      }
    
      Car(String model) {
      this(model, "흰색", 250);	//this() 사용해 마지막 생성자를 호출
      }
    	
      Car(String model, String color) {	//this() 사용해 마지막 생성자를 호출
      this(model, color, 250);
      }
    
      Car(String model, String color, int maxSpeed) {		//마지막 생성자
      this.model = model;			//]
      this.color = color;			//]- 공통 실행 코드
      this.maxSpeed = maxSpeed;		//]
      }
    }
    

3. 메소드 (Method)

객체의 동작에 해당하는 중괄호 { } 블록
외부로부터 매개값을 받을 수 있고, 실행 후 어떤 값을 리턴할 수도 있다.

3-1 메소드 선언

1
2
3
	리턴타입 메소드 이름( [매개변수 선언, ...] ) { //메소드 실행 블록
		실행코드 작성
	} 

리턴타입

메소드가 실행 후 리턴하는 값의 타입, 리턴값이 없을 수도 있고 있을 수도 있다.
메소드 실행 후 결과를 호출한 곳에 넘겨줄 경우 리턴값이 존재해야 한다.

ex) 전자계산기 객체에서 전원을 켜는 powerOn() 메소드와 두 수를 나누는 divide() 메소드 가정한다.
powerON() 메소드는 전원만 키면 된다. divide() 메소드는 리턴 값이 있다고 봐야 한다.

리턴값이 없는 메소드는 리턴 타입에 void 가 와야 하며
리턴값이 있는 메소드는 리턴 값의 타입이 와야 한다.
divide() 메소드의 결과가 double 값이라면 double을 리턴타입으로 사용

1
2
	void powerOn() {...}
	double divide(int x, int y) {...}

리턴값이 있느냐 없느냐에 따라 메소드를 호출하는 방법이 조금 다르다.

1
2
	powerOn();
	double result = divide( 10, 20 );

만약 result 변수를 int 타입으로 선언하게 되면 double 값을 저장할 수 없기 때문에 컴파일 에러 발생한다.

1
	int result = devide( 10, 20 ); //컴파일 에러

리턴값을 반드시 변수에 저장할 필요는 없다.
리턴값이 중요하지 않고, 메소드 실행이 중요할 경우 다름과 같이 변수 선언 없이 호출 할 수 있다.

1
	divide( 10, 20 );   

매개 변수 선언

매개 변수는 메소드가 실행할 때 필요한 데이터를 외부로부터 받기 위해 사용된다.

1
	double divide(int x, int y) {...}

divide() 메소드를 호출 할 때에는 반드시 두 개의 int 값을 주어야 한다.

1
	double result = divide(10, 20);

매개 변수의 수를 모를 경우

매소드의 메개 변수는 이미 정해져 있는 것이 일반적이지만, 경우에 따라서는 메소드를 선언할 때 매개 변수의 개수를 알 수 없는 경우가 있다.

ex) 여러 개의 수를 모두 합산하는 메소드를 선언해야 한다면, 몇 개의 매개 변수가 입력될 지 알 수 없음
해결책 : 매개 변수를 배열 타입으로 선언하는 것

1
	int sum1(int[] values) {	}

sum1() 메소드를 호출할 때 배열을 넘겨줌으로써 배열의 항목 값들을 모두 전달할 수 있다.
배열의 항목 수는 호출할 때 결정된다.

1
2
3
	int[] values = {1, 2, 3};
	int result = sum1{values};
	int result = sum1( new int[] {1, 2, 3, 4, 5} );

매개변수를 배열 타입으로 선언하면, 메소드를 호출하기 전에 배열을 생성하야 하는 불편한 점이 있다.
그래서 배열을 생성하지 않고 값의 리스트만 넘겨주는 방법도 있다.
sum2() 메소드의 매개 변수를 ...를 사용해서 선언하게 되면,
메소드 호출 시 넘겨준 값의 수에 따라 자동으로 배열이 생성, 매개값으로 사용된다.

1
	int sum1(int ... values) {	}

...로 선언된 매개 변수의 값은 메소드 호출 시 리스트로 나열해주면 된다.

1
2
	int result sum2(1, 2, 3);
	int result sum2(1, 2, 3, 4, 5);

...로 선언된 매개 변수는 배열 타입이므로 다음과 같이 배열을 직접 매개값으로 사용해도 좋다.

1
2
3
	int[] values = {1, 2, 3};
	int result = sum2(values);
	int result = sum2(new int[] {1, 2, 3, 4, 5} );

3-2 리턴(return) 문

리턴값이 있는 메소드

return 문의 리턴값은 리턴 타입이거나 리턴 타입으로 변환될 수 있어야 한다.

1
return 리턴값;

ex) int 인 plus() 메소드에서 byte, short, int 타입의 값이 리턴되어도 상관 없이 없다.
byte와 short은 int 로 자동 타입 변환되어 리턴되기 때문이다.

1
2
3
4
int plus(int x, int y) {
	int result = x+y;
	return result;
}
1
2
3
4
int plus(int x, int y) {
	byte result = (byte) (x+y);
	return result;
}

return문을 사용할 때 주의할 점
return 문 이후에 실행문이 오면 "Unreachable code" 컴파일 오류가 발생된다.
return 문 이후의 실행문은 결코 실행되지 않기 때문이다.

ex) 잘못된 코드

1
2
3
4
5
int plus(int x, int y) {
int result = x+y;
return result;
Sysout.out.println(result);	//Unreachable code
}

ex) 컴파일 에러가 발생하지 않는 코드

1
2
3
4
5
6
7
8
boolean isLeftGas() {
	if(gas==0) {
	System.out.println("gas가 없습니다.");
	return false;
}
System.out.println("gas가 있습니다.");	//return false; 다음에 있지만 if문의 조건식이 false 가 될 경우 정상적으로 실행됨.
return true;
}

리턴값이 없는 메소드(void)

void 로 선언된 리턴값이 없는 메소드에서도 return 문을 사용할 수 있다.

1
	return;
1
2
3
4
5
6
7
8
9
10
11
void run() {
	while(true) {
		if(gas > 0) {
		System.out.println( "달립니다.(gas 잔량 : "+gas+")" );
		gas -= 1;
		} else {
			System.out.println( "멈춥니다.(gas 잔량 : "+gas+")" );
			return; //run() 메소드 실행 종료 
		}
	}
}

3-3 메소드 호출

메소드는 클래스 내ㆍ외부의 호출에 의해 실행된다.
클래스 내부의 다른 메소드에서 호출할 경우에는 단순 메소드로 호출하면 된다.
클래스 외부에서 호출할 경우 우선 클래스로부터 객체를 생성한 뒤, 참조변수를 이용해서 메소드를 호출해야 한다.
*이유 : 객체가 존재해야 메소드도 존재한다.

ex) image

객체 내부에서 호출

클래스 내부에서 다른 메소드를 호출할 경우 작성한다.
메소드가 매개 변수를 가지고 있을 때에는 매개 변수의 타입과 수에 맞게 매개값 제공한다.

1
	메소드( 매개값, ...);
  • ex) 객체 내부에서 호출
    1
    2
    3
    4
    5
    6
    7
    8
    
    public class ClassName {
      void method1 ( String p1, int p2 ) {
    		
      }
      void method2() {
          method1("홍길동", 100);	//method1 호출 -> method1 실행
      }
    }
    

    메소드가 리턴값이 없거나, 있어도 받고 싶지 않을 경우 위와 같이 모두 호출이 가능하다.

리턴 값이 있는 메소드를 호출하고 리턴값을 받고 싶을 경우 변수를 선언하고 대입하면 된다.

1
	 타입 변수 = 메소드(매개값, ...);   //메소드에서 타입 변수로 리턴값 대입
  • ex) 리턴 값이 있는 메소드를 호출하고 리턴 값을 받고 싶은 경우
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
      public class ClassName {
          int method (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이 저장
          }
    	
      }
    

객체 외부에서의 호출

외부 클래스에서 메소드를 호출하려면 우선 다음과 같이 클래스로부터 객체를 생성해야 한다.
메소드는 객체에 소속된 멤버이므로 객체가 존재하지 않으면 메소드도 존재하지 않기 때문이다.

1
클래스 참조변수 = new 클래스(매개값, ...);

객체가 생성되었다면 참조 변수와 함께 도트(.) 연산자를 사용해서 메소드를 호출할 수 있다.
도트(.) 연산자는 객체 접근 연산자로 객체가 가지고 있는 필드나 메소드에 접근할 때 사용된다.

1
2
참조변수.메소드( 매개값, ...);			// 리턴값이 없거나, 있어도 리턴값을 받지 않을 경우
타입 변수 = 참조변수.메소드(매개값, ...);		// 리턴값이 있고, 리턴값을 받고 싶을 경우

ex) Car 객체의 keyTurnOn() 메소드와 run() 메소드를 호출하는 코드
keyTurnOn() 과 run() 메소드는 리턴값이 없기 때문에 단순 호출만, getSpeed() 메소드는 리턴값이 있으므로 리턴값을 받아 변수 speed에 저장했다.

1
2
3
4
	Car myCar = new Car();
	myCar.keyTurnOn();
	myCar.run();
	int speed = my Car.getSpeed();

3-4 메소드 오버로딩

클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것을 메소드 오버로딩(overloading) 이라고 한다.
오버로딩의 사전적 의미는 많이 싣는 것을 뜻한다.
하나의 메소드 이름으로 여러 기능을 담응다 하여 붙여진 이름이라 생각할 수 있다.
메소드 오버로딩의 조건은 매개 변수의 타입, 개수, 순서 중 하나가 달라야 한다.

ex)

1
2
3
4
5
6
7
	class 클래스 {
		리턴 타입 메소드이름 ( 타입 변수, ... ) { ... }
		    	    		   
		   무관	   동일		매개변수의 타입, 개수, 순서가 달라야 
		    	    		   
		리턴 타입 메소드이름 ( 타입 변수, ... ) { ... }
	}

메소드 오버로딩이 필요한 이유는 매개값을 다양하게 받아 처리할 수 있도록 하기 위해서이다.

ex)

1
2
3
4
	int plus(int x, int y) {
		int result = x + y;
		return result;
	}	

plus() 메소드를 호출하기 위해서는 두 개의 int 매개값이 필요하다.
하지만 int 타입이 아니라 double 타입의 값을 덧셈하기 위해서는 plus() 메소드를 호출할 수 없다.
해결 방법은 매개 변수가 double 타입으로 선언된 plus() 메소드를 하나 더 선언하는 것이다.

1
2
3
4
	double plus(dounle x, double y) {
		double result = x + y;
		return result;
	}

-> 오버로딩된 메소드를 호출할 경우 JVM 은 매개값의 타입을 보고 메소드를 선택한다. ex) plus() 메소드를 호출하면 plus(int x, int y) 가 실행된다.

1
	plus(10, 20);

그리고 다음과 같이 plus() 메소드를 호출하면 plus(double x, double y) 가 실행된다.

1
	plus(10.5, 20,3);

그렇다면 다음 코드는 어떻게 될까? 컴파일 오류가 날까? 만일 실행이 된다면 어떤 메소드가 실행될까?

1
2
3
	int x = 10;
	double y = 20.3;
	plus(x, y);

첫 번째 매개 변수가 int 타입이고 3두 번재 매개 변수가 double 타입인 plus() 메소드가 없기 때문에 컴파일 오류가 날 것 같지만,
사실은 plus(double x, double y) 메소드가 실행된다.
자바 가상 기계는 일차적으로 매개 변수 타입을 보지만, 매개 변수의 타입이 일치하지 않을 경우,
자동 타입 변환이 가능한지를 검사한다.
첫 번째 매개 변수인 int 타입은 double 타입으로 변환이 가능하므로 최종적으로 plus(double x, double y) 메소드가 실행된다.

메소드를 오버로딩 할 때 주의할 점
매개 변수의 타입과 개수, 순서가 똑같을 경우 매개 변수 이름만 바꾸는 것은 메소드 오버로딩이라고 볼 수 없다.
리턴 타입만 다르고 매개 변수가 동일하다면 이것을 오버로딩이 아니다.

why? 리턴 타입은 자바 가상 기계가 메소드를 선택할 때 아무런 도움을 주지 못하기 때문이다.
아래와 같이 선언했다면 오버로딩이 아니기 때문에 컴파일 오류가 발생한다.

1
2
	int divide(int x, int y) { ... }
	double divide(int doonja, int boonmo) { ... }

메소드 오버로딩의 가장 대표적일 예는 System.out.println() 메소드이다.
println() 메소드는 호출할 때 주어진 매개값의 타입에 따라서 오버로딩된 println() 메소드를 호출한다.

ex) 오버로딩된 println() 메소드

1
2
3
4
5
6
7
8
9
		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(Object x) { ... }
		void println(String x) { ... }

🌞 정보 : 공부 기록용 블로그입니다. 오타나 내용 오류가 있을 경우 알려주시면 감사하겠습니다.

댓글남기기