클래스는 속성(상태)과 기능(동작)을 가지고 있다. 메소드는 이 중에 기능을 담당한다. 속성은 멤버변수가 담당한다.
메소드는 클래스를 new키워드를 이용해 인스턴스화 시켜야 사용할 수 있다. 그 이유는 static main() 메서드가 생성되는 Static 영역, 클래스를 new로 인스턴스화 되는 Heap 영역으로 다르기 때문이다.

main 메소드는 Java 프로그램이 실행 될 때 가장 먼저 호출이 되는 메소드이고 Java 프로그램이 실행될 때 Static영역에 이미 생성되어 있다. 하지만 SpaceInvaders의 경우는 new연산자로 인스턴스를 생성하기 전까지는 존재하지 않는다. 그래서 new를 하기 전에는 호출이 되지 않는 것이다.
public static void main(String[] args) {
SpaceInvaders si = new SpaceInvaders();
si.moveLeft();
}
메서드를 사용하기 위해서는 클래스를 인스턴스화 해야한다.
public class 클래스이름(){
접근제어자 리턴타입 메소드이름(){
// 메소드의 기능
}
}
public class Greet {
public void printHello() {
System.out.println("Hello");
}
}
public class GreetTest {
public static void main(String[] args) {
Greet greet = new Greet(); // 클래스 인스턴스화
greet.printHello();
System.out.println(greet);
}
}
참조변수 Greet타입의 greet변수에는 new Greet() 명령으로 초기화된 Greet인스턴스의 메모리 주소(Greet@5197848c)가 들어있다.
| 출력결과 |
|---|
| Hello |
| com.example.javaproject2.week4.day15.Greet@5197848c |
첫 번째 이유는 기능을 반복 사용하기 위해 만든다.
로또 기계를 예로 들어보겠다.
public class LottoMachine{
public void 숫자섞기(){}
public void 숫자랜덤뽑기(){}
public void 인쇄하기(){}
}
위 기능들을 한번만 사용할 수 있다면 얼마나 불편할까?
위 기능들을 반복해서 사용할 수 있다면 로또 5~100장 등 여러 장을 구매하는데 기다리는 시간도 줄고 판매하는 입장에서도 편하게 판매할 수 있을 것이다.
자바의 클래스는 속성과, 기능을 표현하기 위해 멤버변수와 메소드를 사용한다. 물론 멤버변수만 만들기도 하고 메소드만 만들기도 하지만 대체로는 두가지를 같이 쓰도록 고려해서 클래스를 만들게 된다.
public class User {
String name;
String phoneNumber;
int age;
boolean isAdult() {
return age >= 18;
}
}
중복된 코드를 제거하는 것은 프로그램을 개발하면서 자주 하는 작업이다. 정확히 말하면 중복된 코드를 통합하는 것이다. 리팩토링(Refactoring)이라는 용어로 불린다. 리팩토링은 기능은 그대로 유지하면서 구조를 개선하는 것을 말한다.
중복된 코드를 통합할 때 가장 먼저 사용하는 방법은 반복되는 기능을 메소드로 분리하는 것이다.
public class SeparateToMethod {
public static void main(String[] args) {
int[][] arr = {
{10, 20, 30},
{40, 50, 60},
{70, 80, 90}
};
System.out.println(Arrays.toString(arr[0]));
System.out.println(Arrays.toString(arr[1]));
System.out.println(Arrays.toString(arr[2]));
arr[0][0] = 0;
arr[1][1] = 0;
arr[2][2] = 0;
System.out.println(Arrays.toString(arr[0]));
System.out.println(Arrays.toString(arr[1]));
System.out.println(Arrays.toString(arr[2]));
}
}
위 코드를 보면 아래 3줄이 반복되는 것을 볼 수 있다.
System.out.println(Arrays.toString(arr[0]));
System.out.println(Arrays.toString(arr[1]));
System.out.println(Arrays.toString(arr[2]));
이렇게 반복되는 코드를 메소드로 구현하면 반복 사용하기에도 좋고 깔끔하게 보여 코드를 읽기에 가독성이 더 좋을 것이다.

| 구분 | 내용 |
|---|---|
| public | 모든 클래스에서 접근 가능 다른 패키지에서도 접근 가능 |
| protected | 같은 패키지 내의 클래스와 해당 클래스를 상속한 외부 패키지의 클래스에서만 접근 가능 |
| default | 접근 제어자를 쓰지 않은 경우의 기본값 같은 패키지 내의 클래스에서만 접근 가능 |
| private | 해당 클래스에서만 접근 가능 |
각 접근제어자가 붙어있는 클래스, 메소드, 변수의 사용 가능 범위
| 접근제어자 | 같은 클래스 | 같은 패키지 | 자손 클래스 | 전체 |
|---|---|---|---|---|
| public | O | O | O | O |
| protected | O | O | O | |
| default | O | O | ||
| private | O |
메소드는 변수를 선언할 때 타입을 지정하는 것과 마찬가지로 리턴타입을 꼭 써주어야 한다. 지정할 수 있는 리턴타입은 변수와 같이 원시타입(Primitive Type)과 참조타입(Refrence Type)모두 가능하다. 변수와 다른 점은 메소드는 void를 타입 대신 지정할 수 있다는 점이다.
메소드의 리턴 타입이 변수와 다르면 선언한 변수에 메소드의 실행 결과를 저장할 수 없다.
Scanner sc = new Scanner();
int iNum = sc.nextFloat(); // float을 int에 넣으려는 시도로 불가
float fNum = sc.nextInt(); // int를 float에 넣으려는 시도로 불가
단순히 출력하고 메소드가 끝나는 경우에는 리턴타입이 필요없는 void로 표시하고 사용한다. void는 호출만 가능하고 결과를 변수에 저장할 수 없다.
public class Greet {
public void printHello() {
System.out.println("Hello");
}
}
한 개의 클래스에 메소드를 여러 개 만들 수 있기 때문에 메소드는 다른 메소드와 구분 할 수 있게 이름을 붙여주도록 되어있다. 메소드 이름을 지을 때도 역시나 그 메소드의 기능을 유추 할 수 있도록 짓는 것이 좋다.
메소드의 이름은 카멜케이스(Camel Case)를 사용한다. 클래스 이름을 지을때도 카멜케이스를 사용했지만 클래스 이름을 지을때와는 조금 다르게 소문자로 시작한다. ex) printHello(), nextInt()
메소드는 '기능'이기 때문에 메소드 이름은 '동사'로 짓거나 동사를 포함해서 짓는다.
boolean으로 리턴하는 경우에는 앞에 is를 붙인다. ex) isAdult()
리턴(Return)은 메소드를 호출한 곳으로 연산 결과를 보내고 싶을 때 사용한다.
public class 클래스이름 {
접근제어자 리턴타입 메소드이름(){
return 리턴타입_결과;
}
}
public class 클래스이름Test {
public static void main(String[] args){
클래스이름 변수이름 = new 클래스이름();
리턴타입 새변수이름 = 변수이름.메소드이름();
}
}
매개변수는 Parameter를 번역한 말로 메소드를 호출하는 곳에서 메소드로 값이나 오브젝트를 전달하는 매개체가 된다고 하여 매개변수라고 한다.
매개변수로는 int, float등의 원시타입(Primitive Type)뿐만 아니라 String, User, int[] 등의 참조타입(Refrence Type)도 가능하다.
앞에서 메소드를 설명 할 때는 접근제어자, 리턴타입, 메소드명 3가지만 언급 했지만 메소드를 구성하는 것은 매개변수까지 총 4가지라고 할 수 있다. 매개변수는 메소드를 선언 하면서 ()안에 추가 할 수 있다.

사용 예시
public class Calculator {
public void plus() { // 생략 }
public void printPlusOne(int num) {
System.out.println(num + 1);
}
public static void main(String[] args) {
Calculator calculator = new Calculator();
calculator.printPlusOne(30);
calculator.printPlusOne(100);
calculator.printPlusOne(350);
}
}
| 출력 결과 |
|---|
| 31 |
| 101 |
| 351 |

calculator.printPlusOne(30)이 실행될 때 30을 전달한다.printPlusOne(int num)으로 전달한다..printPlusOne(int num)으로 전달된 30은 System.out.println(num + 1);로 전달된다.매개변수는 2개 이상 여러 개의 매개변수를 받을 수 있다.
public class Calculator {
// 생략
public void printPlus(int num1, int num2) {
System.out.println(num1 + num2);
}
public static void main(String[] args) {
Calculator calculator = new Calculator();
calculator.printPlus(10, 20);
calculator.printPlus(20, 30);
}
}
| 출력 결과 |
|---|
| 30 |
| 50 |
메소드를 호출 하려면 new를 이용해 메소드가 들어있는 클래스의 인스턴스를 생성해야만 호출 할 수 있다. 하지만 static을 붙이면 JVM이 실행 될때 인스턴스를 생성하므로 new를 이용해 인스턴스를 생성하지 않고도 main() 메소드에서 바로 메소드를 호출 할 수 있다.
스태틱 메소드를 선언하는 방법은 기존 메소드 선언하는 방식에서 리턴타입 앞에 static을 붙이면 된다.
public class 클래스이름{
접근제어자 static 리턴타입 메소드이름(){
// 메소드의 기능 구현
}
}
스태틱 메소드의 호출 방법은 기존에 new를 이용해 인스턴스를 생성하는 방법과는 다르게 바로 호출 할 수 있다.
public class Calculator {
// 생략
public static void printMinus(int num1, int num2) {
System.out.println(num1 - num2);
}
public static void main(String[] args) {
printMinus(10, 20); // 인스턴스 생성 없이 바로 호출
}
}
스태틱 메소드는 접근제어자를 public으로 해놓았다면 다른 클래스에서도 바로 호출해서 쓸 수 있다.
아래 코드와 같이 스태틱 메소드를 사용하면 new 를 쓰지 않고도 다른 클래스에서 호출할 수 있다. 다만 호출하려는 스태틱 메소드가 어떤 클래스에 선언된 메소드인지를 지정해주어야 한다.
public class CalculatorTest {
public static void main(String[] args) {
Calculator.printMinus(10, 20); // Calculator 클래스에 선언됨
}
}
public class PreDefinedStaticMethod {
public static void main(String[] args) {
Integer.parseInt("30"); // Integer 클래스에 선언됨
Math.pow(2, 10); // Math 클래스에 선언됨
}
}
아래 코드와 같이 main 메서드를 실행하면 배열을 출력할 때 중복이 발생한다. 이를 스태틱 메소드를 이용해 코드를 분리해 보겠다.
import java.util.Arrays;
public class SeparateToMethod {
public static void main(String[] args) {
int[][] arr = {
{10, 20, 30},
{40, 50, 60},
{70, 80, 90}
};
System.out.println(Arrays.toString(arr[0]));
System.out.println(Arrays.toString(arr[1]));
System.out.println(Arrays.toString(arr[2]));
arr[0][0] = 0;
arr[1][1] = 0;
arr[2][2] = 0;
System.out.println(Arrays.toString(arr[0]));
System.out.println(Arrays.toString(arr[1]));
System.out.println(Arrays.toString(arr[2]));
}
}
main 메소드에서 스태틱 메소드가 호출되는지 확인
import java.util.Arrays;
public class SeparateToMethod {
public static void printArr(){
System.out.println("printArray입니다.");
}
public static void main(String[] args) {
// 생략
printArr();
}
}
System.out.println("printArray입니다."); → 호출 확인 했으므로 삭제
import java.util.Arrays;
public class SeparateToMethod {
public static void printArr(){
System.out.println(Arrays.toString(arr[0]));
System.out.println(Arrays.toString(arr[1]));
System.out.println(Arrays.toString(arr[2]));
}
public static void main(String[] args) {
// 생략
printArr();
}
}
arr배열의 0~2행을 출력하기 위해 2차원 배열 int[][]을 매개변수로 받기
import java.util.Arrays;
public class SeparateToMethod3 {
public static void printArray(int[][] arr) {
System.out.println(Arrays.toString(arr[0]));
System.out.println(Arrays.toString(arr[1]));
System.out.println(Arrays.toString(arr[2]));
}
// 생략
}
출력 결과 별로 구분하기 위해 스태틱 메소드에 System.out.println("-----------"); 추가함
import java.util.Arrays;
public class SeparateToMethod3 {
public static void printArray(int[][] arr) {
System.out.println(Arrays.toString(arr[0]));
System.out.println(Arrays.toString(arr[1]));
System.out.println(Arrays.toString(arr[2]));
System.out.println("-----------");
}
public static void main(String[] args) {
int[][] arr = {
{10, 20, 30},
{40, 50, 60},
{70, 80, 90}
};
printArray(arr);
arr[0][0] = 0;
arr[1][1] = 0;
arr[2][2] = 0;
printArray(arr);
arr[0][2] = 0;
arr[1][1] = 0;
arr[0][2] = 0;
printArray(arr);
}
}
| 출력 결과 |
|---|
| [10, 20, 30] |
| [40, 50, 60] |
| [70, 80, 90] |
| --------- |
| [0, 20, 30] |
| [40, 0, 60] |
| [70, 80, 0] |
| --------- |
| [0, 20, 0] |
| [40, 0, 60] |
| [70, 80, 0] |
| -------— |