Ch6 객체지향프로그래밍 - 메소드
메소드의 구조
접근지정자 리턴타입 메소드명(매개변수){ //메소드 선언부를 메소드 시그니처라고도 한다.
수행문
}
cf. 접근지정자 : 메소드의 사용 범위를 정한다 [public package(default) protected private]
수업시간 코드
package p1;
//클래스는 설계도이기 때문에 객체화하지 않으면 소용이 없음.
public class Calculator {
/*메소드의 구조
접근지정자 리턴타입 메소드명(매개변수) {
//접근지정자 메소드의 사용 범위를 정함 public package(default) protected private
//이 패키지 안에서 클래스 상관없이 다 써라(default).
//동작할 내용 코드
}
메소드는 정의해두고 사용할때는 호출해서 쓴다.
리턴타입 변수명 = 메소드명(매개변수)*/
int number1 = 10;
int number2 = 20;
double hap; //선언된 변수의 자료형과 리턴할 값의 자료형이 일치하거나 자동변환 가능해야한다.
//메소드 정의한 것 만든 것
public void sum() { //리턴타입이 void가 아닐 경우에는 리턴할 자료가 없더라도 return문을 생략할 수 없다.
hap = number1 + number2;
System.out.println("합은" + hap);
// return hap;
}
}
리턴타입
보통 실행 후 리턴값을 호출한 곳으로 돌려줘야 할 때 리턴타입을 지정한다. 따라서 리턴 타입은 있을 수도 있고 없을 수도 있다.
리턴타입에 따른 메소드 사용방법(정의와 호출)이 다르다.
또한 리턴타입이 있더라도 리턴값을 담을 변수를 선언할수도 안할수도 있다.
1. 리턴타입이 있는 경우 : 메소드 실행문에 메소드를 종료시킬 return문이 필요하다.
return 리턴값;
1-1. 실행클래스에 담아줄 변수를 선언일반적으로 리턴타입을 설정하면 실행클래스에 리턴값을 담아줄 변수를 선언한다.
이 때 리턴값을 담아줄 변수의 타입과 메소드의 리턴타입은 ①일치하거나 ②자동 형변환이 가능해야 한다.
실행클래스.리턴값을 담는 변수의 타입 >= 라이브러리클래스.메소드의 리턴타입
1-2. 실행클래스에 담아줄 변수를 선언하지 않음 : 리턴타입이 있다고 해서 반드시 리턴값을 변수에 저장할 필요는 없다. 리턴값이 중요하지 않고, 메소드 실행이 중요할 경우에는 리턴값을 저장할 변수를 선언하지 않는다. (이 경우에도 선언문에는 리턴값을 지정해준다)
2. 리턴타입이 없는 경우(void)
마찬가지로 메소드의 실행만이 중요한 경우에 사용하며 void로 표기한다. 리턴값이 없는 메소드에서도 메소드 실행을 강제 종료시키기 위해 return문을 사용할 수 있다. (사용하지 않아도 된다)
return;
(단, while문 뒤에 실행문이 추가적으로 더 있을 경우 return문이 아닌 break문을 사용해야 한다.)
//라이브러리 클래스 (메소드의 정의)
public class Calculator{
double divide(int x, int y){ ... return ~;} //1. 리턴 타입이 double
int plus(int x, int y) {... return ~;} //1. 리턴 타입이 int
void powerOn() { ... } //2. 리턴타입이 없는 경우
}
========================================
//실행클래스 (메소드의 호출)
public class CalculatorExample{
public static void main(String[] args){
Calculator cl = new Calculator();
cl.powerOn();
//1-1.담아줄 변수가 있을 때
// int result = cl.divide(10,20); (x) 컴파일 에러 (리턴타입 double과 담아줄 변수타입 int의 불일치)
double result = cl.divide(10,20); (o) ①일치
double result = cl.plus(10,20); (o) ②담아줄 변수타입이 메소드의 리턴타입보다 크다면 오류안남
//1-2.담아줄 변수가 없을 때
cl.divide(5,8); //메소드의 실행이 중요한 경우
//2. 리턴타입이 없는 경우 (=사실상 1-2와 동일)
cl.powerOn();
매개변수선언
매개값의 타입과 매개변수의 타입은 일치하거나, 달라도 매개값의 타입이 매개변수의 타입으로 자동 타입 변환이 가능해야한다.
//라이브러리 클래스 (lib)
double divide(int x, int y) { ... } //매개변수의 타입 : int
//실행 클래스
double result = lib.divide(10, 20); (0)
double result = lib.divide(10.5, 20.4); (x) //매개값의 타입이 매개변수의 타입보다 크기 때문에 컴파일 오류
byte b1 = 10, b2 = 20;
double result = divide(b1, b2); (o) //매개값의 타입이 매개변수의 타입보다 작기 때문에 정상 호출
매개변수의 수를 모를 경우 (가변크기배열)
메소드를 선언하는데 매개 변수의 개수를 결정할 수 없을 때 매개변수로 배열을 사용한다.
배열을 매개변수로 가지는 메소드 선언
1. int sum1(int[ ] values) { }
2. int sum2(int ... values) { } // ...로 선언된 메소드의 매개변수는 배열 타입을 가진다 (즉 1=2)
배열을 매개변수로 가지는 메소드 호출
1. int[ ] values = {1,2,3};
int result = sum1(values); //배열을 생성한 뒤 배열명을 매개변수로 가지는 메소드 호출 방식
int result = sum1(new int[ ] {1,2,3,4,5}) //매개변수에 배열 생성 키워드(new int[])를 포함한 메소드 호출 방식
2. int result = sum2(1,2,3);
//...로 선언된 메소드를 호출할 때는 매개변수에 직접 배열의 값을 넣을 수 있다. 메소드 호출 시 자동으로 매개변수를 값으로 가진 배열을 생성한다.
//일반적이진 않지만 ...로 선언된 메소드는 배열타입을 가지기 때문에 1을 통한 메소드 호출도 가능하다.
방법1 - 정의
int sum1(int[] values) {
int sum=0;
for(int i=0; i<values.length; i++) {
sum+= values[i];
}
return sum; //배열의합계
}
방법1 - 호출
int[] values1 = {1,2,3};
int result1 = myCom.sum1(values1);
System.out.println("result1 : " + result1);
int result2 = myCom.sum1(new int[] {1,2,3,4,5});
System.out.println("result2 :" + result2);
방법2 - 정의
int sum2(int ... values) {
int sum=0;
for(int i=0;i<values.length;i++) {
sum += values[i];
}
return sum;//배열의 합계
}
//... : 매개변수에 "..."를 사용해서 선언하면 메소드 호출시 넘겨준 값의 수에 따라서 배열이 생성된다.
//매개변수의 수를 모를때(가변일때)
//myCom.sum2(1,2,3); → 일반 매개변수처럼 넣어도 알아서 배열이 됨
방법2 - 호출
//...을 사용한 메소드 선언 시 호출 방법은 3가지다.
int result3 = myCom.sum2(1,2,3); //가장 일반적이고 단순
System.out.println(result3);
int[] values1 = {1,2,3,4}; //기존의 방법을 쓸 수 있다.
int result4 = myCom.sum2(values1);
System.out.println("result4 : " + result4);
int result5 = myCom.sum2(new int[] {1,2,3,4,5});
System.out.println("result5 :" + result5);
메소드와 생성자의 차이점
메소드 동작 : 호출할때마다 수행한다. 메모리가 늘어나진 않는다. 여러번 수행해야 할 때 사용한다.
생성자 동작 : 생성자 호출 시 객체 생성(메모리에 올릴 때, new) 할 때 한번만 수행된다. 한정된 메모리를 아껴써야하기 때문에 메소드동작과 나누어 사용하는데, 주로 초기값을 셋팅해줄때와 실행하자마자 네트워크를 연결해줄 때 쓴다.
메소드 오버로딩
클래스 내에 같은 이름의 메소드를 여러개 선언하는 것. 매개변수의 타입, 순서, 개수는 달라야 한다.
단, 리턴타입은 영향을 주지 않는다.
(리턴타입이 달라도 매개변수의 타입,순서,개수가 같다면 같은 메소드이기 때문에 컴파일 오류)
오버로딩된 메소드를 호출할 경우 JVM이 매개값의 타입을 보고 메소드를 선택한다.
정수와 실수처럼 매개변수의 타입이 서로 다른 경우에도 연산이 가능한데,
이는 매개변수의 타입이 일치하지 않을 경우 자동타입 변환이 가능한지를 검사하기 때문이다.
//라이브러리클래스
int plus(int x,int y) {
return = x+y;
}
double plus(double x, double y) {
return = x+y;
}
//실행클래스
int x = 10;
double y = 20.3;
lib.plus(x,y); //오류가 날 것 같지만 plus(double x, double y)메소드가 실행된다.
int number1 = 10;
int number2 = 20;
double hap;
//메소드명이 동일하게 여러개 사용하는 것 : 메소드 오버로딩
//메소드 오버로딩 규칙 : 1.메소드명이 동일 2.매개변수가 달라야 한다(타입, 순서, 갯수) 3.리턴타입은 상관없다(달라도 된다). 단,매개변수 타입이 같은데 리턴타입만 다른 건 안된다
//합계구하기 정수형 2개
int sum(int n1, int n2) {
return n1+n2;
}
/* double sum(int n1, int n2) { //리턴타입이 달라도 매개변수타입과 개수가 같기 때문에 오버로딩불가.
return n1+n2;
}*/
double sum(int n1, double n2) { //정수와 실수의 연산도 가능하다.
return n1+n2;
}
double sum(double n1, int n2) { //변수의 순서가 다르면 괜찮다.
return n1+n2;
}
//합계구하기 실수형 2개
double sum(double n1, double n2, double n3) {
return n1+n2+n3;
}
//합계구하기 정수형3개
int sum(int n1, int n2, int n3) {
return n1+n2+n3;
}
//합계구하기 문자형2개
String sum(String s1, String s2) {
return s1+s2;
}
}
메소드 오버라이딩
상속의 개념에서 부모클래스의 메소드를 자식의 클래스의 메소드로 재정의하는 것
package p1;
public class ClassEx01 {
public static void main(String[] args) {
Calculator calculator = new Calculator();
// calculator.sum(); //메소드호출
/* calculator.hap = calculator.sum(); //hap에다 넣어주고 싶을 때
calculator.printHap();
*/
calculator.sum(1, 3);
calculator.sum(1, 15.5);
calculator.sum(1.1, 2);
calculator.sum(10.5,20.5,30.5);
calculator.sum(10,20,30);
calculator.sum("Java", "Study");
}
}
인스턴스 멤버와 this
인스턴스(instance) 멤버란 객체(인스턴스)를 생성한 후 사용할 수 있는 필드와 메소드를 말하는데, 이들을 각각 인스턴스 필드, 인스턴스 메소드라고 부른다. 실행 클래스에서 인스턴스 멤버를 만들고 접근하기 위해서는 필히 객체를 생성하고, 참조변수를 통해 접근해야 한다.
객체 내부에서도 인스턴스 멤버에 접근하기 위해 일종의 참조변수를 사용하는데, 이것이 this이다. 주로 생성자와 메소드의 매개변수의 이름이 필드와 동일한 경우 인스턴스 멤버를 가리키기 위해 사용된다.
인스턴스 필드 gas는 객체마다 따로 존재하고, 인스턴스 메소드 setSpeed()는 메소드 영역에만 저장되고 존재한다.
27 인스턴스 멤버와 this
package BookExampleHomeWork;
public class Car4 {
//필드
String model;
int speed;
//생성자
Car4(String model){
this.model = model;
}
//메소드
void setSpeed(int speed) {
this.speed = speed;
}
void run() { //사실 이 메소드에서 this.를 지정하든 말든 결과값은 변하지 않는다.
for(int i=10; i<=50; i+=10) { //다만, 가독성을 위해 this를 붙인걸로 추측된다.
this.setSpeed(i);
System.out.println(this.model + "가 달립니다. (시속:" + this.speed + "km/h)");
}
}
}
===============================
28 인스턴스 멤버와 this
package BookExampleHomeWork;
public class CarExample4 {
public static void main(String[] args) {
Car4 myCar = new Car4("포르쉐");
Car4 yourCar = new Car4("벤츠");
myCar.run();
yourCar.run();
}
}
메소드를 통한 출력
일반적으로 부품클래스 사용하는 방법 설명 (가장 많이 사용하는 방법은 방법3)
요구사항은 속성안 값을 넣기
방법1 String name="홍길동"
방법2 생성자를 이용해서 넣기
Student(String name){
this.name = name;
}
--- main() 부분
Student st = new Student("홍길동");
방법3 메소드를 이용해서 넣기
void nameInput(String name) {
this.name = name;
}
--- main() 부분 메소드 호출
st1.nameInput("홍길동");
===============================================
클래스안에 필드에 있는 것을 사용(출력)하는 방법(일반적으로 많이 사용하는 방법은 방법3)
//확실하진 않지만, 메소드를 가장 많이 사용
방법1
Student st = new Student();
System.out.println(st.name);
방법2 생성자를 이용해서 출력
Output(){
System.out.println(name);
}
--- main() 부분
Student st = new Student();
st.Output();
방법3 메소드를 이용해서 출력
void output(){
System.out.println(output);
}
--- main() 부분
Student st = new Student();
st.output();
s1.toString()
//참조변수를 출력하면 해쉬코드가 출력된다.
출처 : 164 도서관리프로그램(인터페이스 추상클래스, Iterator)