자바 초보부터 B형까지6 -함수 작성법 필수-

1. 함수 작성법

 

별 모양 *을 5번 한줄로 찍어주는 함수를 print5Stars라고 이름짓고 이를 작성할려고 한다면 어떻게 해야할까

 

public static void print5Stars() {
    코드 작성
}

 

자바의 기본 골격인 main함수가 static으로 정의되어 있어서, 함수 선언시에 반드시 static을 사용해야한다.

 

또한 함수를 정의하기 위해 자바에서는 반환타입을 반드시 명시해야한다.

 

public static (반환타입) (함수이름)(인자) { 내용 } 의 형태이다.

 

함수의 반환값이 없다면, void로 반환타입을 명시한다.

 

자바에서 함수 이름은 소문자로 시작하여, 의미 단위로 단어가 시작될때 대문자로 적는 camelCase를 따른다.

 

5개의 *을 출력해주는 함수는..

 

public static void print5Stars() {
    for(int i = 0; i < 5; i++){
        System.out.print("*");
    }
    System.out.println();
}

 

 

이렇게 정의한 함수를 실행할때는 (함수이름)(); 형태로 적어주면 된다.

 

만약 별 5개를 4줄로 출력해야하는 문제는 다음과 같이 작성할 수 있다.

 

public class Main {
    public static void print5Stars() {
        for(int i = 0; i < 5; i++){
            System.out.print("*");
        }
        System.out.println();
    }
    
    public static void main(String[] args){
        for(int i = 0; i < 4; i++){
            print5Stars();
        }
    }
}

>> *****
   *****
   *****
   *****

 

 

2. 인자를 받는 함수

 

정수 n,m을 받아 숫자 1로 채워진 n*m 직사각형을 출력

 

당연하지만 정의된 인자만큼 정확히 넘겨줘야한다.

 

n,m 2개를 받는데 n 1개만 넘기면 에러

 

import java.util.Scanner;

public class Main {

    public static void printRect(int n, int m){
        for(int i = 0; i<n; i++){
            for(int j = 0; j<m; j++){
                System.out.print("1");
            }
            System.out.println();
        }
    }
    public static void main(String[] args) {
        // 여기에 코드를 작성해주세요.

        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        int m = sc.nextInt();

        printRect(n,m);
    }
}

2 3

111
111

 

 

3. 유클리드 호제법으로 최대공약수 구하는 함수 작성하기

 

파이썬으로 작성했던 유클리드 호제법을 떠올리며...

 

a,b = b, a%b

 

import java.util.Scanner;

public class Main {

    public static void gcd(int n, int m){

        while (m != 0){
            
            int q = m;
            int r = n%m;

            n = q;
            m = r;

        }
        System.out.println(n);
    }
    public static void main(String[] args) {
        // 여기에 코드를 작성해주세요.
        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        int m = sc.nextInt();

        gcd(n,m);

    }
}

12 18
6

 

 

두 정수 a,b의 최대공약수가 g이면.. a = kg, b = xg 의 형태로 나타낼 수 있다.

 

여기서 두 수 a,b의 최소공배수는... lcm(a,b) = kxg이다.

 

import java.util.Scanner;

public class Main {

    public static void lcm(int n, int m){

        int a = n;
        int b = m;

        while (m != 0){
            int q = m;
            int r = n % m;

            n = q;
            m = r;
        }

        System.out.println(a/n * b/n * n);

    }
    public static void main(String[] args) {
        // 여기에 코드를 작성해주세요.

        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        int m = sc.nextInt();

        lcm(n,m);
    }
}

12 18 
36

 

 

4. 값을 반환하는 함수

 

함수명 앞에 반환타입을 반드시 명시해야하며, 반환하는 값은 return 뒤에 써준다.

 

return문 뒤에도 반드시 ;을 써야함

 

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

 

int 타입 a+b를 return하니까 int라고 반환타입을 명시

 

함수는 return을 만나면 return을 수행한 뒤에 모든 수행을 마치고 즉시 종료한다.

 

 

5. 함수 안에 함수

 

함수 안에 또 다른 함수를 호출하는 것은 당연히 가능하다

 

public class Main {
    public static boolean allDifferent(int n) {
        return (n / 10) != (n % 10);
    }

    public static boolean isMagicNumber(int n) {
        return n % 3 != 0 && allDifferent(n);
    }
    
    public static void main(String[] args) {
        int cnt = 0;
        for(int i = 10; i < 100; i++)
            if(isMagicNumber(i))
                cnt++;

        System.out.println(cnt);
    }
}

>> 54

 

 

6. call by value

 

자바에서는 함수에 인자를 넘길때 값을 복사해서 넘겨준다.

 

public class Main {
    public static void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
        System.out.println(a + " " + b);
    }

    public static void main(String[] args) {
        int n = 10, m = 20;
        swap(n, m);
        System.out.println(n + " " + m);
    }
}

 

위와 같이 swap에 n,m을 넣었더니 20 10이 나오고 다시 n,m을 출력해보면 여전히 10 20인 것을 알 수 있는데

 

이는 n,m 자체가 넘어가는 것이 아니고, n,m에 적힌 10,20이 a,b라는 새로운 변수에 복사되어 넘어가기 때문이다.

 

함수 안에서는 a,b가 바뀌어 20 10으로 나왔지만, 함수 밖에서는 아무런 변화가 없어서 10 20이 출력되는 것이다.

 

그러면 함수 호출 이후에 실제 n,m을 바뀌게 하는 방법은 없는가?

 

결론은 불가능하다.

 

JVM 메모리에서는 int, char, double같은 기본형 변수는 변수와 값 모두 stack 영역에 저장

 

String, Integer, Array 같은 reference type 같은 경우 값은 heap 영역에 저장되고, stack 영역의 변수가 값의 주소를 저장한다.

 

 

이건 뭐 배웠던거니까...

 

그러면 뭐 아무튼.. 위에서 a,b로 인자를 받았으니까 복사되어 실제 아무런 변화가 없는거 아니냐?

 

그렇다면 n,m으로 인자를 받으면 실제로도 바꿀 수 있는거 아니냐?

 

이렇게 생각할 수 있는데

 

class IntWrapper {
    int value;

    public IntWrapper(int value) {
        this.value = value;
    }
}

public class Main {
    public static void modify(IntWrapper n, IntWrapper m) {
        n.value = 50;
        m = new IntWrapper(60);
        System.out.println(n.value + " " + m.value);
    }

    public static void main(String[] args) {
        IntWrapper n = new IntWrapper(10);
        IntWrapper m = new IntWrapper(20);
        modify(n, m);
        System.out.println(n.value + " " + m.value);
    }
}

 

위 결과는 어떨까?

 

50 60

50 20이라고 한다.

 

처음에 main 함수가 수행될때, intwrapper n,m이 정의되면서 얘들은 reference type이니까 heap에 값이 저장되어 stack의 변수가 이들을 가리키는 주소를 저장

 

 

다음 modify(n,m)으로 n,m을 넘겨주는데...

 

이 때 reference type인 n,m을 넘기면.. 해당 변수의 값을 가리키는 새로운 변수가 stack에 새롭게 정의된다.

 

 

그래서 항상 자바는 call by value 원칙을 따른다고 한다.

 

아무튼 modify 함수 내에서 n.value = 50;으로 n의 value를 바꾸는데..

 

현재 main의 n과 modify 내의 n이 동일한 값을 가리키고 있으므로 modify 내에서 수정한 값이 main에서도 그대로 적용된다

 

그런데 m = new IntWrapper(60);으로 새로운 객체를 heap영역에 생성하고, m이 이 객체를 가리키게 된다

 

 

따라서 main에서 m은 modify에서 아무리 바꿔도 변동하지 않는다

 

따라서 자바에서 swap함수를 구현하고 싶다면.. 다음과 같이 reference type을 새롭게 정의하여

 

main과 함수 내의 n,m이 동일한 객체를 가리키도록 만들어서 값을 바꿔줘야한다.

 

class IntWrapper {
    int value;

    public IntWrapper(int value) {
        this.value = value;
    }
}

public class Main {
    public static void swap(IntWrapper n, IntWrapper m) {
        int temp = n.value;
        n.value = m.value;
        m.value = temp;
    }

    public static void main(String[] args) {
        IntWrapper n = new IntWrapper(10);
        IntWrapper m = new IntWrapper(20);

        swap(n, m);

        System.out.println(n.value + " " + m.value); // 20 10
    }
}

 

 

자꾸 new를 빼먹는데 heap에 객체를 생성할려면 new를 써줘야한다

 

IntWrapper n = new IntWrapper(10);
IntWrapper m = new IntWrapper(20);

 

 

 

 

7. call by reference

 

만약 배열을 인자로 넘기고 싶다면 어떻게 해야할까?

 

정수 배열의 타입은 int[]이다. 인자 타입에 int[]라고 명시해주면 된다

 

public class Main {
    public static void modify(int[] arr2) {
        arr2[0] = 10;
    }

    public static void main(String[] args) {
        int[] arr = new int[]{1, 2, 3, 4};
        modify(arr);

        for(int i = 0; i < 4; i++)
            System.out.print(arr[i] + " ");
    }
}

 

함수 내에서 배열을 변경하고 main에서 배열을 출력해보면 어떨까?

 

위의 경우 [1,2,3,4]가 아니라 [10,2,3,4]가 나온다.

 

이는 배열이 reference type이기 때문이다.

 

stack에 정의된 arr2 변수와 main의 arr변수가 heap영역의 동일한 [1,2,3,4]를 가리키기 때문에,

 

함수 내에서 변경하면 main에서도 당연히 변경된다

 

이를 방지하는 하나의 방법은 배열을 깊은 복사하여 넘기는 것이다.

 

.clone() 메소드는 배열을 복사해 새로운 배열을 생성한다.

 

public class Main {
    public static void modify(int[] arr2) {  // arr2는 arr와 관련이 없다.
        arr2[0] = 10;
    }

    public static void main(String[] args) {
        int[] arr = new int[]{1, 2, 3, 4};
        modify(arr.clone());                 // 새로운 배열을 만들어 넘기기

        for(int i = 0; i < 4; i++)
            System.out.print(arr[i] + " ");
    }
}

>> 1 2 3 4 # 값에 변화가 없다

 

 

8. 문자열의 특성

 

문자열 String은 reference type이긴 하지만 immutable한 특징이 있다.

 

한번 생성된 문자열 값은 직접적으로 바꿀수 없다

 

그래서 함수 modify내에서 문자열 s를 바꾸더라도, main에서 문자열 s는 바뀌지 않는다.

 

public class Main {
    public static void modify(String s) {
        s += "apple";
    }

    public static void main(String[] args) {
        String str = "banana";
        modify(str);
        System.out.print(str);
    }
}

 

그러면 실제로 바꾸는 방법은? 아주 단순하게 함수 modify에서 변경된 s를 return하고

 

main에서 return된 값을 받아와 변수에 덮어씌우면 된다

 

public class Main {
    public static String modify(String s) {
        s += "apple";
        return s;
    }

    public static void main(String[] args) {
        String str = "banana";
        str = modify(str);
        System.out.print(str);
    }
}

>> bananaapple
TAGS.

Comments