일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 |
- linux
- 자료형
- github
- 코테
- OVH
- PR
- 서버오류
- string
- Leetcode
- 백준
- 큐
- JAVA기초
- github cli
- elasticbeanstalk
- Queue
- 논리학
- 서버중단
- Java
- 데이터센터
- LiveTemplate
- AWS
- 통신대란
- thymeleaf
- springboot
- 명제
- Intellij
- char[]
- random
- 자동완성
- 코딩테스트
- Today
- Total
Midnight Coder's Lounge
[Java] public static void main(String[] args)는 무슨 뜻인가요? 본문
요약
main 함수는 자바 프로그램의 시작점이다.
자바가상머신(JVM)은 main이라는 이름을 가진 메서드를 찾아 프로그램을 시작한다.
- public : JVM이 main함수를 찾을 수 있도록 한다.
- static : JVM이 main함수를 곧바로 실행할 수 있도록 한다.
- void : main함수가 종료되면 프로그램도 종료되므로, return값이 필요하지 않다.
- String[] args : 커맨드라인 등을 통해, main함수 내부에서 사용할 수 있는 String 데이터를 전달할 수 있다.
상세
모든 Java 프로그램은 main 함수(메서드)부터 시작합니다.
그렇기 때문에 Java를 배우신 분들이라면,
Java를 처음 시작하여 "Hello World"를 출력할 때부터 main 함수를 접해오셨을 겁니다.
public class Hello{
public static void main(String args[]){
System.out.println("Hello World");
}
}
Java의 Hello World
이제 막 Hello World를 치기 시작했을 때를 돌아보면
시작부터 뜻도 모르는 단어가 너무 많아서 당황스러웠을 텐데요.
(파이썬이라면 print('hello world') 한 줄로 끝낼 내용인데 말이죠...)
main 함수에 잔뜩 붙어 있는 이 단어들은 대체 무슨 의미를 갖는 걸까요?
📄 Java Language Specification, 12.1.4. [Invoke Test.main]
"The method main must be declared public, static, and void. It must specify a formal parameter (§8.4.1) whose declared type is array of String. Therefore, either of the following declarations is acceptable:"
"메인 메서드는 반드시 public, static, void로 선언하고, 그 매개변수는 String 배열 타입으로 선언해야 합니다.
따라서, 다음과 같이 선언하는 것이 합당합니다."
public static void main(String[] args)
public static void main(String... args)
[사전지식]
아직 다음 용어를 한 번도 들어보신 적이 없다면, 어느 정도 공부를 하신 다음 돌아오셔서 이 글을 보시기 바랍니다.
- JVM
- 객체지향
- 클래스와 인스턴스
- 메서드 / 메소드
- 접근 제어자
- return 타입
- 매개변수
📍 public과 static : JVM의 main 메서드 탐색과 실행
익히 들어보셨듯이 자바 프로그램은 자바 가상 머신(JVM)Java Virtual Machine을 통해 실행됩니다.
JVM은 프로그래머가 선언해 놓은 다양한 클래스들 중에서
main이라는 이름이 붙은 메서드를 찾아 프로그램을 시작하도록 설계되어 있는데,
이 때 JVM이 main 메서드를 올바르게 식별하고 실행할 수 있도록
main 메서드에는 알맞은 제어자modifier가 부착되어 있어야 합니다.
자유로운 접근을 허용하는 public
객체지향 개념을 배울 때 자세히 배우게 되는, 접근 제어자access modifier의 일종인 public입니다.
접근 제어자는 특정 함수(메서드)에
'클래스 내부에서만 접근할 수 있다, 패키지 내부에서만 접근할 수 있다'
등등 접근 제약을 설정하는 역할을 하는데요.
그 중 public은 '접근 제한이 없고, 자바 프로젝트 내에서 자유롭게 접근할 수 있다'는 의미를 나타냅니다.
- default(기본값, 접근 제어자를 명시하지 않음) : 같은 패키지 내에서만 접근 가능.
- private : 같은 클래스 내에서만 접근 가능.
- protected : 같은 패키지 + 다른 패키지의 자식 클래스에서 접근 가능.
- public : 접근 제한 없음.
[예시]
Example이라는 클래스를 하나 만들고, 서로 다른 접근 제어자를 가진 메서드 4개를 선언하겠습니다.
그런 다음, 패키지가 서로 다르고, 상속 관계도 없는 별개의 클래스인 Access를 만들고,
Example 클래스가 가진 메서드를 Access 클래스 내부에서 호출해 보겠습니다.
package alpha;
public class Example {
//alpha 패키지에 속해있는 Example이라는 클래스를 만들어 보았습니다.
//protected 메서드 "prot"을 선언합니다.
protected void prot() { }
//default 메서드 "def"를 선언합니다.
void def() { }
//private 메서드 "priv"를 선언합니다.
private void priv() { }
//public 메서드 "pub"를 선언합니다.
public void pub() { }
}
package bravo;
import alpha.Example;
public class Access {
//bravo라는 패키지에 속해 있는 클래스 Access를 만들어 보았습니다.
public static void main(String[] args) {
Example e = new Example();
//앞서 만든 클래스 Example의 새 인스턴스를 생성하고,
//Example의 메서드를 호출해 보겠습니다.
e.prot();
e.def();
e.priv();
//protected, default, private에 해당하는 메서드는 호출되지 않습니다.
//[에러 메시지:]"The method OOO() from the type Example is not visible"
e.pub();
//public에 해당되는 메서드는 정상적으로 호출됩니다.
}
}
IntelliJ나 Eclipse 등 실제 IDE에서 코드를 작성해 보면,
protected, default, private에 해당하는 메서드는 Access 클래스 내에서 호출되지 않습니다.
속해 있는 패키지가 다른 별개의 클래스에서는
오직 public에 해당하는 메서드만 호출되는 것을 확인할 수 있습니다.
이처럼 클래스나 패키지 외부에서 특정 메서드에 자유롭게 접근하려면,
메서드를 public으로 선언해야 한다는 것을 알 수 있습니다.
[ main 메서드의 경우 ]
JVM이 이제 막 Java 프로그램을 시작하고 main 메서드를 실행하려 하는 시점은
아직 어떤 클래스도 로드Load되어 있지 않은 상태입니다.
가장 먼저 main 메서드가 어떤 클래스에 있는지 찾아서, 해당하는 클래스를 찾아서 불러오는 과정이 필요합니다.
이 때 만약에 main 함수에 아무 접근 제어자도 달려 있지 않거나(default),
private 또는 protected가 붙어 있다면,
'클래스 내부에서만 접근을 허용한다, 패키지 내부에서만 접근을 허용한다'
등등 접근 제약이 생기게 될 것입니다.
그렇다면 아직 아무런 클래스를 불러오지도 못한 JVM은
당연히 접근 제약을 통과하지 못해 main 메서드를 찾지도 못하고,
프로그램을 시작조차 할 수 없게 되겠죠.
이런 일이 생기지 않아야 하므로,
main 메소드에 아무런 접근 제약이 존재하지 않도록 public을 붙여주어야 하는 것입니다.
인스턴스 생성이 필요없는 static
일반적으로 클래스에 선언한 속성이나 메서드를 사용하기 위해서는,
먼저 해당 클래스의 인스턴스를 생성한 다음 접근해야 합니다.
앞의 예제를 다시 보겠습니다.
Example e = new Example();
//Example의 새 인스턴스를 생성하고, 참조변수 e가 해당 인스턴스를 가리키게 하였습니다.
e.pub();
//참조변수 e를 통해 Example 클래스의 pub() 메서드에 접근했습니다.
하지만 속성이나 메서드를 static으로 선언한다면
클래스의 인스턴스를 생성하지 않더라도 호출할 수 있다는 특성을 갖습니다.
static으로 선언한 속성과 메서드는 어차피 인스턴스와 상관없이 항상 일정한 값을 가지니까,
굳이 인스턴스를 생성하지 않아도 사용할 수 있게끔 만들어진 것이죠.
[예시]
package alpha;
public class Example {
public static void pub() { }
//앞의 예제에 있던 public 메서드 pub()에 static을 붙여주었습니다.
}
package bravo;
import alpha.Example;
public class Access {
public static void main(String[] args) {
//Example e = new Example();
//Example 클래스의 새 인스턴스를 생성하는 코드는 주석처리합니다.
Example.pub();
//pub() 메서드에 static이 붙었으므로, 인스턴스 없이 클래스 이름만으로 즉시 에러 없이 호출됩니다.
}
}
IDE에서 코드를 작성해 보면,
static 메서드는 인스턴스를 생성하고, 참조변수로 접근하는 과정 없이
클래스 이름만으로 바로 접근이 가능한 것을 확인할 수 있습니다.
[ main 메서드의 경우 ]
JVM이 Java 프로젝트 내에서 main 메서드를 발견하면, main 메서드가 들어있는 클래스를 로드하게 됩니다.
그 후 따로 인스턴스를 생성하는 과정을 거치지 않고,
클래스가 로드되면 곧바로 main메서드를사용할 수 있도록 static을 붙여주어야 합니다.
📍 return값이 없는 반환 타입 void
return문은 메서드의 실행이 종료되었을 때,
메서드의 실행 결과를 전달해주는 역할을 수행합니다.
이 때, 메서드의 실행 결과는 해당 메서드를 호출한 또다른 메서드로 전달됩니다.
[예시]
public class CallEx {
public static void main(String[] args) {
int result = firstMethod();
//firstMethod()를 호출합니다.
//firstMethod()가 return한 결과를 int 변수 result에 대입합니다.
System.out.println(result);
//result를 출력합니다.
}
static int firstMethod() {
return secondMethod() + 20;
//secondMethod()를 호출합니다.
//secondMethod()가 return한 결과에 20을 더하여, 자신을 호출한 main 메서드에게 그 값을 반환합니다.
}
static int secondMethod() {
return 10;
//자신을 호출한 firstMethod()에게 10을 반환합니다.
}
}
출력 결과 :
30
위 코드의 실행 과정을 다음과 같이 그림으로 표현할 수 있겠습니다.
[ main 메서드의 경우 ]
그런데 main 메서드는 어떨까요? 굳이 반환 타입이 필요할까요?
다른 모든 메서드가 실행을 마치면 이제 남은 메서드는 main 메서드뿐이고,
main 메서드가 종료되면 그대로 프로그램도 종료됩니다.
더 이상 실행할 메서드도 없는데,
main 메서드가 어떤 결괏값을 만들어 내든 사용해 볼 기회조차 없을 겁니다.
결국 main 메서드는 반환값이 필요하지 않기 때문에,
아무런 반환값을 return하지 않도록 반환 타입을 void로 선언하는 것입니다.
📍 매개변수 String[] args
📄 Java Language Specification, 12.1. [Java Virtual Machine Startup]
"The Java Virtual Machine starts execution by invoking the method main of some specified class, passing it a single argument, which is an array of strings"
"자바 가상 머신(JVM)은 특정 클래스의 메인 메서드를 호출함으로써 프로그램 실행을 시작하는데,
이 때 String 배열 타입의 인자 하나를 전달합니다."
매개변수는 메서드가 기능을 수행하기 위해 필요한 값(인자, argument)들을 받아오는 변수입니다.
main 메서드도 마찬가지로, args라는 이름을 가진 String 배열 타입의 매개변수를 가지며,
여러 개의 문자열을 입력받아 프로그래머가 지정한 기능을 수행할 수 있습니다.
Tip :
- 반드시 매개변수명을 args로 쓸 필요는 없으며, 때때로 'argv'라고 쓰는 프로그래머도 있습니다.
- String[]은 String...으로 대체할 수 있으며 같은 의미입니다. (아주 오래된 Java 버전에서는 불가)
하지만 실제로 Java 프로그램을 만들면서 이 args라는 매개변수를 사용해 본 적이 있으신가요?
요즘같이 IntelliJ나 Eclipse같은 IDE로 Java를 공부하는 시대에는
main 메서드에 문자열을 전달할 일이 정말 손에 꼽을 정도로 드물 겁니다.
사실 이건 Java가 DOS환경에서 실행되던 시절의 흔적이기도 한데요,
다음 예제를 통해 명령 프롬프트로 Java 코드를 실행하여, 문자열 인자를 main 메서드에 전달해 보도록 하겠습니다.
[예제]
코드 :
package test;
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
System.out.println("총 " + args.length + "개의 값을 입력받았습니다.");
//전달받은 배열 인자가 가진 String 갯수를 출력합니다.
System.out.println("입력 내용 : " + Arrays.toString(args));
//전달받은 배열 인자의 내용을 출력합니다.
}
}
명령 프롬프트로 실행 :
* javac Example.java -encoding UTF-8
→ .java 파일을 JVM이 이해할 수 있도록 .class파일로 변환합니다(컴파일).
* java test.Example aa bb cc
→ test 폴더에 들어있는 Example.class를 실행합니다. main함수에 aa, bb, cc를 String 배열 형태로 전달합니다.
실행 결과
총 3개의 값을 입력받았습니다.
입력 내용 : [aa, bb, cc]
보시는 바와 같이 main 메서드가 전달받는다는 String 배열은,
바로 사용자가 커맨드라인에 입력하는 문자열들을 가리키는 것이었습니다.
예제 코드가 실행된 결과,
커맨드라인에 입력한 문자열들(aa, bb, cc)의 갯수와 그 내용이 출력된 것을 확인할 수 있었습니다.
Tip : intelliJ나 Eclipse 같은 IDE에서도 각각 설정을 통해 main 메서드에 String 배열 인자를 전달할 수 있습니다.
마치며
이처럼 낯설고 복잡했던 main함수의 코드에
Java 객체지향 프로그래밍의 주요 원리가 숨어있던 것을 알아볼 수 있었습니다.
Java 프로그램의 실행 과정에 대해 더욱 자세히 알고 싶다면,
다음 공식 문서를 참고해 주세요.
Java Language Specification
https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.2
[참고자료]
- Java의 정석, 3rd Edition, 남궁성
- https://www.quora.com/Why-do-Java-programs-start-from-only-main-string-args-method
- https://www.geeksforgeeks.org/java-main-method-public-static-void-main-string-args/
(수정 : 2024.08.26)
'Language' 카테고리의 다른 글
[Java] Queue의 .add()와 .offer()는 뭐가 다른가요? (2) | 2024.07.22 |
---|---|
[Java] Random으로 Hello World 출력하기 (0) | 2022.10.14 |
[번역][Java] 패스워드에 String이 아닌 char[]를 사용해야 하는 이유 (0) | 2022.08.27 |
[Java] String 변수를 == 로 비교할 때 : 똑같은 값인데, 왜 false가 나오죠? (0) | 2022.08.13 |