주니 개발 도서관

자바(JAVA)

(6)메소드, 재귀함수

주니홍 2022. 6. 14. 12:07
0614 정리

메소드

함수 ( ) 를 의미한다

메인 메소드 밖, 패키지 안에서 사용한다

 

함수 3요소
1. input  -> 있는지 없는지 있다면 무슨자료형인지
2. output -> 있는지 없는지 있다면 무슨자료형인지
3. 기능    -> 기능을 유추할수있도록 작성한다
메소드 시그니쳐 : void printE()

 

메소드를 만들때는

intput 호출할때 넣어줄  입력값
output return으로 반환해줄 출력값

input 과 output 이 있고 없고로 다양한 메소드를 만들 수 있다

 

함수를 만들면서 생각해야하는 순서가 있는데 이를 "모듈화" 라고 할 수 있다

 

모듈화
1. 메소드 시그니처 기본으로 선언
2. 기능을 구현함
3. 기능을 위한 in,output이 있는지 생각
4. 원래 위치에서 모듈화한 과정을 호출

 


 

1
2
3
static void func() {// input X  output X
    System.out.println("내가만든 첫번째 함수!!");
}
cs

static  = 객체와 무관하게

void = 무 ( return 값이 없다. 즉, output 없다. )

func ( ) = 사용할 객체명

 

메소드 { } 안에 있는 것을 사용하고 종료된다

 

 


 

1
2
3
    static void func2(int num) { // input O  output X
        System.out.println("호출할때 받은 num : "+num);
    }
cs
  • func ( 자료형 변수명 )
    이 메소드를 호출(사용)하는 곳에서 값을 넣어줘야 사용이 가능하다
    이 때, 입력받을 값을 자료형에 맞춰서 입력해 주어야 한다 

  • 위의 코드는 int값을 받으려고 하고있고
    • 메소드 안의 num의 값을 아무리 변형 시켜도
      호출한 곳의 같은 변수명 num에는 아무런 변화가 없다 
    • 메소드 안의 num은 메소드 안에서만 사용하는 변수일 뿐이며, 변수을 a로 바꿔도 상관없다

 

 

1
2
3
4
5
6
7
8
9
10
11
12
static void add (int a, String b) { // 종류
    System.out.println(a+b);
}
static void add (String a, int b) { // 순서
    System.out.println(a+b);
}
static void add (int a, int b) { // 갯수
    System.out.println(a+b);
}
static void add (int a, int b, int c) { // 수
    System.out.println(a+b+c);
}
cs
  • 입력값의 갯수는 상관없으며 자료형과 함께 " , "로 구분한다

  • 이 때, " 함수명 중복정의 허용 " 가능한 부분이 있는데
    add 라는 메소드에 입력값의 종류 or 갯수 or 순서중복 정의가 가능하다


  • 종류, 갯수, 순서의 상황을 여러가지로 중복정의를 해보았다

  • 사용할때도 메소드에 맞게 종류, 갯수, 순서를 맞춰주어야 한다 ** 

 

 

이러한 " 중복정의 "를

오버로딩 (overloading)

이라고 한다

 

 


 

1
2
3
static int func3() { // input X  output O
    return 123;
}
cs
  • output을 사용할땐 항상 return함께 사용해 주어야한다
  • return을 사용할땐 void 대신 반환해줄 값의 자료형을 적어준다

main 에서 위의 코드를 사용하게 되면

int a = func3() ;

int a = 123 ;

과 같은 의미의 코드가 된다

 

** int a = func3(); 에서 순서는 중요시 해야한다

  1.  func3(); " 먼저 "호출되어 위의 메소드 풀이대로 자료형 int값의 123으로 return 해준다
  2.  = 대입연산자가 사용되어 int값 a에 123이 대입된다 

 


 

1
2
3
4
static int func4(int a, int b) { // input O  output O      
    int res = a+b;
    return res;
}
cs
  • 중요한 것은 a 와 b는 func4( )메소드의 안에서만 쓰이고 삭제되는 변수라는 것
    • func4 ( 10 ,  30 ) ; 을 입력받았다고 생각해보자
      • int a 에는 10 , int b 에는 30 의 변수에 저장된다
      • int res = 10 + 30; 즉, 40이 res에 저장된다
      • return res ;  res안에있는 40의 값을 return ( int값 ) 한다
    • func4 ( 10 , 30 ) ; ==  40
      • 출력해보았을때 boolean 타입의 true가 출력된다. 즉, 같은 " "이다

 


 

그렇다면 메소드를 사용할때 장점이 무엇일까?

코드의 재사용성이 증가된다

  • 오류의 파급효과가 줄어듦
    • 메소드 안의 코드를 수정하면 호출한 곳이 모두 수정되기 때문
  • 유지보수 용이


메소드를 사용할 때 조심할 점

  • input ,  output 을 이해하고 올바르게 사용해야한다
    • input
      • 중복정의가 가능하다는 점을 이용하여 사용한다 (가독성**)
    • output
      • return 이 있어야하며, return 해줄 자료형을 적어주어야 한다
  • 호출할때의 입력값의 변수명과 헷깔려선 안된다
    • main안의 int a 와  호출하여 메소드 안에서 사용된 int a 는
      같은 변수명이지만, 다른 변수라는 것을 꼭 기억하자

 


 

 

재귀 함수 

함수에서 자기 자신을 다시 호출해 작업을 수행하는 방식이다.

그렇기에 특정 분기까지 자기 자신을 계속해서 호출하는데

주로 반복문을 구현할 때 사용한다.

대표적으로 팩토리얼이 이에 해당한다

 

팩토리얼의 기억해둘만한 특징(공식)이 있다

N! = N × (N-1)!

4! = 4 × 3! = 24

3! = 3 × 2!

1! = 1

 

이를 함수에 이용해 보자

 

1
2
3
    static int fac(int n) {
        return n*fac(n-1);
    }
cs

 

main 메소드안에 출력해보자

System.out.println(fac(5));

 

 

java.lang.StackOverFlowError

함수를 호출할 때 함수의 파라미터(매개변수), 리턴 값, 복귀 주소 등을 스택에 저장한다.

재귀 함수를 사용하면 호출한 함수가 종료되지 않은 채 새로운 함수를 호출하므로

스택 메모리계속적으로 저장되게 되어 스택 메모리에

더 이상 가용 메모리가 없을 경우 스택 오버 플로우가 발생하게 된다.

 

n-1 구간이 반복되면서 음수가 되어도 계속 호출하기 때문에 무한반복됨

해결방법은 종료조건을 만들어주는 것이다!

1! = 1 이기때문에 n == 1에 도착할때 return 값으로 1을 돌려주면 된다

 

1
2
3
4
5
6
7
    static int fac(int n) {
        
        if (n == 1) { // 종료조건 n=1 -> 1! = 1 이기 떄문
            return 1;
        }
        return n*fac(n-1); // "종료조건"이 필요
    }
cs

 

( 그림에선 n = 0까지 가버렸지만 갈필요는 없다! )**

 

그림순서

  • fac(5) 함수 호출
  • 5xfac(4) 의 값을 return 하려했으나 값을 몰라서 못함
  • fac(4)값을 모르기에 호출
  • 4xfac(3) 의 값을 return 하려했으나 값을 몰라서 못함
  • fac(3)값을 모르기에 호출
    ...
  • fac(1)값을 모르기에 호출
  • if ( n == 1 ) 만난다
    return 1; 하여 무한반복을 제어한다!

 

 


 

객체를 이용한 메소드 사용시 유의사항

 

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
26
27
28
    static void func1(int i) {
        i+=10;
    }
    static void func2(int[] arr) {
        arr[0= -100;
        arr[arr.length-1]=-123;
        // 주소 값을 가져와서 바꾼거라서 변경이된다
    }
    
    
    public static void main(String[] args) {
        
        int num = 10;
        // main공간에 바로 저장
        
        int [] data= {1,2,3,4,5};
        // 객체가 따로 저장되는 곳에 먼저 저장하고
        // main공간에는 객체에 저장된 "주소"를 가져온것
        System.out.println(data);
        // 출력결과물이 "주소"
        
        func1(num);
        System.out.println("num = " +num);
        
        func2(data);
        for(int i = 0; i < data.length; i++) {
            System.out.print(data[i] + " ");
        }
cs

 

 

메소드를 이용하여

객체의 값을 입력받아서 메소드를 이용하게 되면

 (Heap)에 저장되있는 주소값 즉, 힙에

저장된 값의 주소값을 스택(Stack)에 저장한다

 

그렇기에 메소드에 이용되는 객체에 저장값은 주소값이므로

주소값으로부터의 변형을 준다면 변형된다.

 

(변수는 메소드의 변수 이름이 같아도 서로 다른 변수이다

ㅡ> 변수는 이와 관계없음

 

 

그렇기에 위의 코드 출력값은 아래와 같다 꼭 유의하도록 하자

 

 

 


 

 

메소드 이용하여

 

"재사용성"과 "가독성"을 이용해보자

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
        Random rand=new Random();
        int[] data=new int[5];
 
        for(int i=0;i<data.length;i++) {
            data[i]=rand.nextInt(100)+1;
        }
 
        for(int i=0;i<data.length;i++) {
            System.out.print(data[i]+" ");
        }
 
        System.out.println();
 
        for(int i=0;i<data.length;i++) {
            if(data[i] % 2 == 0) {
                System.out.print("짝수 ");
            }else {
                System.out.print("홀수 ");
            }
        }
cs

 

 

[모듈화]

모듈화 란 기능적인 분리를 통해서 다른 프로그램을 개발할 때에도

유지보수와 코드 재사용성을 높여 소프트웨어를 설계하는 기법을 일컫는다

 

문제

랜덤한 1~100의 정수를 배열 int [ 5 ] 에 for문을 이용하여 초기화 및 출력

해당 정수값들이 짝수라면 "짝수" 홀수라면 "홀수" 출력 하는 코드이다

 

이때, 짝수가 나왔을 경우에 "짝수"출력 딸랑 한줄이 아닌

100줄 이상의 코드를 사용해야하는 곳이라고 생각해보자

"홀수"도 마찬가지이다

 

메인메소드에 조건문이 100줄이상 있는것은 "가독성"에 좋지않다

그래서 따로 메소드를 이용해보자

 

 

메소드를 작성해보기 전 생각해봐야 할 것들이 있다


[ 모듈화 과정 ]

  • 메소드 시그니처를 기본으로 선언

    • input, output, 기능과 관련된 이름 (함수 3요소)

  • 기능을 구현한다

  • 기능을 위한 input, output이 있는지를 생각후 설정

  • 원래 위치에서 모듈화한 과정을 호출하여 사용

사용할 메소드를 생각해보며 위의 과정을 표현해보자

"짝수" 와 "홀수"를 출력해줄 것이다

ㅡ> "짝수"와 "홀수"는 현재 100줄 넘는 코드로 가정한다

 

  • void print () {  }  메소드 시그니처 기본으로 선언

  • 만약 짝수라면 "짝수"출력 홀수라면 "홀수"출력

  • input으로 값을 받아야 확인할 수 있을 것 같고
    output은 출력만하기때문에 필요없을 것 같다

  • 사용할 위치에서 호출

1
2
3
4
5
6
7
8
9
    static void print( input ) {
        
        if (??) { // ?? 일때 짝수이다
            System.out.println("짝수!");    
        }else {// ??일때 짝수이다
            System.out.println("홀수!");    
        }
        
    }
cs

 

생각해볼 것은

input 에는 무엇이 오는게 좋을지

조건문에는 무엇을 이용하면 좋을지다

 

  • 짝수를 구하는법은 정수%2 == 0 일경우에 짝수 아니라면 "홀수"이다

  • input에 정수를 받아오면 좋을것 같다

 

 

1
2
3
4
5
6
7
8
9
    static void print(int num) {
        
        if (num % 2 == 0) { // 나머지가 0이라면 짝수 이다
            System.out.println("짝수!");    
        }else {// 나머지가 0이 아니라면 홀수 이다
            System.out.println("홀수!");    
        }
        
    }
cs

 

출력해보자

 

사용법은 해당 메소드의 이름과 ( 자료형에 맞는 input 값 )을 적어주면된다

 

print 라는 이름이였고 괄호 안에 data[ i ]를 이용하여

배열안의 값을 차례로 넣어줬다

 

콘솔에 입력한 숫자가 짝수라면 짝수 홀수라면 홀수가 출력되었다

 


위와 비슷한 내용이며

 

해당 메소드는 output값을 이용하여 사용해 보았다

 

 

'자바(JAVA)' 카테고리의 다른 글

(8) 퀵정렬  (0) 2022.06.19
(7) 클래스  (0) 2022.06.15
(5) 배열과 배열관련 알고리즘  (0) 2022.06.13
(주말) 영상과제  (0) 2022.06.13
0607~0610 Exception(오류) 정리  (0) 2022.06.11