switch 문
switch (값) {} 값으로 가능한 값
# 흐름 제어문 - switch 문법
public class Exam0230 {
public static void main(String[] args) {
// switch (값) {}
// 값으로 가능한 데이터 타입은?
// => int 정수(byte,short,int,char), 문자열, 특별한 상수 Enum 타입
// => case 값으로 변수를 사용할 수 없음 -> 리터럴만 가능
byte b = 2;
switch (b) {
case 1:
case 2:
default:
}
short s = 2;
switch (s) {
case 1:
case 2:
default:
}
int i = 2;
switch (i) {
case 1:
case 2:
default:
}
char c = 'A'; // A문자의 유니코드 값(UTF-16) 0x41(65)을 c에 저장
switch (c) {
// case 의 값도 int 값이면 무엇이든 됨
case 'A': // 0x41 = 65
case 66:
case 0x43:
default:
}
// String 값을 switch와 case의 값으로 사용할 수 있음
String str = "hello";
switch (str) {
// case 의 값으로 String 가능
case "hello":
case "ohora":
case "hul":
default:
}
}
}
public class Exam0231 {
public static void main(String[] args) {
// switch (값) {} 값으로 가능한 데이터 타입
// => int 정수(byte,short,int,char), 문자열, 특별한 상수 Enum 타입
// => case 값으로 변수를 사용할 수 없음 -> 리터럴만 가능
// case에는 리터럴만 올 수 있음
// 즉 변수를 사용할 수 없음
int x = 1, y = 300;
switch (x) {
case 1 * 300: // OK
// case 1 * y: // 컴파일 오류!
}
}
}
// - 4바이트를 넘어가는 정수는 사용할 수 없음
// - 부동소수점은 사용할 수 없음
// - boolean 값을 switch와 case에 사용할 수 없음
final 을 이용하여 코드 깔끔하게 만들기
# 흐름 제어문 - switch 문법 II
public class Exam0241 {
public static void main(String[] args) {
int level = 1;
// 상수를 사용하면 주석없이 바로 이해할 수 있음
// => case 문자의 값으로 변수를 사용할 수 없음
// => 단 값이 변경되지 않는 final 변수라면 사용할 수 있음
final int GUEST = 0, MEMBER = 1, ADMIN = 2;
switch (level) {
case GUEST:
System.out.println("조회만 가능합니다.");
break;
case MEMBER:
System.out.println("글작성 가능합니다.");
break;
case ADMIN:
System.out.println("다른 회원의 글을 변경, 삭제할 수 있습니다.");
break;
}
}
}
ㄴ final 을 이용하여 int 상수 만들기
enum 타입
# 흐름 제어문 - switch 문법 II
public class Exam0242 {
// 상수를 좀 더 조직적으로 관리하는 방법
// => enum을 사용하여 상수를 정의함
// => nested enum은 기본이 static 이므로 static을 생략해도 됨
enum Level {
GUEST, MEMBER, ADMIN
}
public static void main(String[] args) {
// enum으로 정의된 상수를 사용하려면 enum 타입의 변수를 선언해야 함
// => final int처럼 직접 값을 지정하지 않아도 됨
// => 값을 직접 지정할 수도 있음
// enum을 사용하는 주된 이유
// => 100, 200, "admin" 과 같이 값을 직접 지정할 필요가 없음
// => enum 변수에는 그 타입에 정의된 값만 저장할 수 있음
// => 안전한 코드를 작성할 수 있음
Level level = Level.MEMBER;
// 다음과 같이 switch나 case 값으로 enum 타입의 값이 올 수 있음
switch (level) {
case GUEST:
System.out.println("조회만 가능합니다.");
break;
case MEMBER:
System.out.println("글작성 가능합니다.");
break;
case ADMIN:
System.out.println("다른 회원의 글을 변경, 삭제할 수 있습니다.");
break;
}
}
}
=> case 안에는 Level.level 이라고 해줄 필요 없음
ㄴ enum 은 특별한 상수타입으로 정의
while 에서의 break continue
# 흐름 제어문 - break와 continue 활용
public class Exam0321 {
public static void main(String[] args) {
int count = 0;
int sum = 0;
// 1부터 100까지의 짝수의 합은?
// => continue 사용 전
count = 0;
sum = 0;
while (count < 100) {
count++;
if ((count & 1) == 0) { // count & 1 ==> count & 0x01 ==> count % 2
sum += count;
}
}
System.out.printf("count=%d, sum=%d\n", count, sum);
System.out.println("------------------------");
// => continue 사용 후
count = 0;
sum = 0;
while (count < 100) {
count++;
if (count % 2 == 1)
continue; // 다음 문장을 실행하지 않고 즉시 조건 검사로 이동
sum += count;
}
System.out.printf("count=%d, sum=%d\n", count, sum);
}
}
ㄴ while 문 안에서 전위연산자나 후위연산자나 기능은 같지만 후위연산자를 많이 씀
ㄴ 짝수 여부 확인할 때 count & 1 사용하는 게 더 좋음
ㄴ continue // 다음 문장을 실행하지 않고 즉시 조건 검사(조건문)로 이동함
중첩 반복문 break
# 흐름 제어문 - 중첩된 반복문 탈출
public class Exam0330 {
public static void main(String[] args) {
int x = 2, y = 1;
// 5 * 5 까지만 출력하라
while (x <= 9) {
while (y <= 9) {
System.out.printf("%d * %d = %d\n", x, y, x * y);
if (x == 5 && y == 5)
break; // 이 break는 자신이 소속된 가장 가까운 반복문을 나감
y++;
}
System.out.println();
x++;
y = 1;
}
System.out.println("종료!!");
}
}
ㄴ break; => 가장 가까운 반복문을 나감
중첩 반복문 break - label 붙이기
# 흐름 제어문 - 중첩된 반복문 탈출
public class Exam0331 {
public static void main(String[] args) {
int x = 2, y = 1;
// 라벨명: 반복문1 { 반복문2 {break 라벨명;}}
// 라벨 문법:
// 라벨: 문장;
// 라벨: {문장1, 문장2, ...}
myloop:
while (x <= 9) {
while (y <= 9) {
System.out.printf("%d * %d = %d\n", x, y, x * y);
if (x == 5 && y == 5)
break myloop; // myloop 라벨에 소속된 문장을 나감
y++;
}
System.out.println();
x++;
y = 1;
}
System.out.println("종료!!");
System.out.println("-----------------------------");
}
}
ㄴ 라벨 붙이기
ㄴ 라벨 묶기 => 중괄호로 묶기 가능
ㄴ break 라벨명;
for 반복문
# 흐름 제어문 - for 반복문
public class Exam0410 {
public static void main(String[] args) {
// for (변수선언 및 초기화; 조건; 증감문) 문장;
// for (변수선언 및 초기화; 조건; 증감문) {문장1; 문장2; ...}
// for 문의 전형적인 예
for (int i = 1; i <= 5; i++)
System.out.println(i);
// 실행 순서
// 1) 변수초기화 => int i = 1
// 2) 조건 => i <= 5
// 3) 문장 => System.out.print(i + " ")
// 4) 변수증가문 => i++
// 조건이 참인 동안 2 ~ 4를 반복
// for 문에서 선언한 변수는 그 for 문 안에서만 사용할 수 있음
// System.out.println(i); // 컴파일 오류!
}
}
ㄴ 조건 거짓이면 for 문 나감
for( ; ; ) => 무한루프
# 흐름 제어문 - for 반복문
public class Exam0413 {
public static void main(String[] args) {
// for (변수선언 및 초기화; 조건; 증감문) 문장;
// for (변수선언 및 초기화; 조건; 증감문) {문장1; 문장2; ...}
// 조건문 제거
int i = 1;
for ( ; ; ) {
if (i > 5)
break;
System.out.println(i);
i++;
}
}
}
for 중첩과 continue
# 흐름 제어문 - for 중첩과 continue
public class Exam0433 {
public static void main(String[] args) {
// continue
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= i; j++) {
if (j % 2 == 0)
continue; // 다음 줄로 가지 않고 '변수증가문'으로 이동
System.out.print(j + " ");
}
System.out.println();
}
}
}
ㄴ continue 만나면 증감으로 이동
for( ; ; ) 와 배열
package com.eomcs.lang.ex06;
// # 흐름 제어문 - for(;;) 와 배열
//
public class Exam0440 {
public static void main(String[] args) {
// String[] names = new String[5];
// names[0] = "홍길동";
// names[1] = "임꺽정";
// names[2] = "유관순";
// names[3] = "윤봉길";
// names[4] = "안중근";
// String[] names;
// names = new String[] {"홍길동", "임꺽정", "유관순", "윤봉길", "안중근"};
// 배열 변수 선언과 동시에 배열 초기화를 실행할 때는 new String[] 을 생략할 수 있다.
String[] names = {"홍길동", "임꺽정", "유관순", "윤봉길", "안중근"};
for (int i = 0; i < names.length; i++)
System.out.println(names[i]);
}
}
for(:) 와 배열
package com.eomcs.lang.ex06;
// # 흐름 제어문 - for(:) 와 배열
//
public class Exam0450 {
public static void main(String[] args) {
String[] names = {"홍길동", "임꺽정", "유관순", "윤봉길", "안중근"};
// 배열의 처음부터 끝까지 값을 꺼내는 것이라면
// 다음의 for 문법을 사용하라! 아주 편하다!
// for (배열에서 꺼낸 값을 저장할 변수 선언 : 배열주소) 문장;
for (String name : names)
System.out.println(name);
// 위의 문장은 컴파일하면 아래의 문장으로 변경된다.
// for (int i = 0; i < names.length; i++) {
// String name = names[i];
// System.out.println(name);
// }
}
}
//
// # for (:)
// - 배열 전체를 반복하거나 컬렉션 객체(java.util.Iterable 구현체) 전체를 반복할 때 유용한다.
// - 배열의 일부만 반복할 수 없다.
// - 배열의 값을 다룰 때 인덱스를 사용할 필요가 없어 편리하다.
//
// 문법:
// for (변수 선언 : 배열, Iterable 구현체) 문장1;
// for (변수 선언 : 배열, Iterable 구현체) { 문장1; 문장2; ...}
// - 변수의 타입은 배열이나 Iterable 구현체의 항목 타입과 같아야 한다.
// - 반복문을 돌 때 마다 항목을 값을 꺼내 변수에 담는다.
String.format
# 메서드 : 개념 및 기본 문법 IV
public class Exam0240 {
// 4) 메서드 : 리턴값(O), 파라미터(O)
// => "이 돈 갖고 과자좀 사와!"
static String hello(String name, int age) {
String retVal = String.format("%d살 %s님을 환영합니다!", age, name);
return retVal;
}
public static void main(String[] args) {
// hello() 메서드를 실행하고, 그 리턴 값을 변수에 담음
String r = hello("홍길동", 20);
System.out.println(r);
// 앞의 예제와 마찬가지로 리턴 값을 한 번만 사용한다면, 사용할 곳에 메서드 호출 코드를 둬라
// => 리팩토링 기법 중에서 "replace temp with query" 라 부름
System.out.println(hello("홍길동", 20));
// 리턴 값을 안 받아도 됨
hello("임꺽정", 30); // 리턴 값은 버려진다.
}
}
=> printf 와 String.format 의 차이
ㄴ printf : 문자열을 만들어서 출력
ㄴ String.format : 문자열을 만드느 것 까지만 함
리팩토링
import java.util.Scanner;
# 메서드 : 사용 후
public class Exam0120 {
// 스페이스를 출력하는 코드들을 관리하기 쉽도록 별도의 블록에 모아 놓음
// 그리고 그 블록에 대해 이름을 붙임
// => 이렇게 정의한 블록을 "메서드(method)" 또는 "함수(function)"이라 부름
// => 자바는 "함수" 보다는 주로 "메서드"라는 이름으로 부름
// => 메서드 이름은 명령 형태의 동사구로 지음
// 예) getName(), setName(), printName(), doFilter(), parseInt() 등
// => 물론 명사구나 전치사구 형태로 짓는 경우도 있음
// 예) valueOf(), toString() 등
//
static void printSpaces(int len) {
for (int i = 0; i < len; i++) {
System.out.print(" ");
}
}
// '*' 문자를 출력하는 코드를 관리하기 쉽게 별도의 블록으로 빼둠
// 그리고 그 블록의 이름을 붙임
// 이렇게 별도로 빼둔 코드 블록에 이름을 붙인 것을 "메서드=함수"라고 부름
//
static void printStars(int len) {
for (int i = 0; i < len; i++) {
System.out.print("*");
}
}
public static void main(String[] args) {
Scanner keyScan = new Scanner(System.in);
System.out.print("밑변의 길이? ");
int len = keyScan.nextInt();
keyScan.close();
for (int starLen = 1; starLen <= len; starLen += 2) {
// 명령 코드들을 기능 별로 묶어 놓고 필요할 때마다 다음과 같이 사용하면 코드를 읽기가 쉬워짐
printSpaces((len - starLen) / 2);
printStars(starLen);
System.out.println();
}
}
}
import java.util.Scanner;
# 메서드 : 리팩토링
public class Exam0130 {
public static void printSpaces(int len) {
for (int i = 0; i < len; i++) {
System.out.print(" ");
}
}
public static void printStars(int len) {
for (int i = 0; i < len; i++) {
System.out.print("*");
}
}
// 코드를 유지보수하기 쉽도록 가능한 기능 별로 묶어둠
// 그래서 Exam0120에 있던 코드 중에서 공백을 계산하는 코드를 별도의 블록으로 분리하여 이름을 부여함
public static int getSpaceLength(int totalStar, int displayStar) {
return (totalStar - displayStar) / 2;
}
public static void main(String[] args) {
Scanner keyScan = new Scanner(System.in);
System.out.print("밑변의 길이? ");
int len = keyScan.nextInt();
for (int starLen = 1; starLen <= len; starLen += 2) {
// 출력할 스페이스의 개수를 계산하는 코드를 블록에 묶어 놓고 이름을 부여해두고 사용하면 코드를 이해하기가 더 쉬움
printSpaces(getSpaceLength(len, starLen));
printStars(starLen);
System.out.println();
}
keyScan.close();
}
}
가변 파라미터
# 메서드 : 가변 파라미터
public class Exam0250 {
// 가변 파라미터
// [리턴타입] 메서드명(타입... 변수) {...}
// => 0 개 이상의 값을 받을 때 선언하는 방식
// => 메서드 내부에서는 배열처럼 사용
//
// 다음은 hello()를 호출할 때 String 값을 0개 이상 전달할 수 있음
static void hello(String... names) {
for (int i = 0; i < names.length; i++) {
System.out.printf("%s님 반갑습니다.\n", names[i]);
}
}
public static void main(String[] args) {
hello(); // 이 경우 names 배열의 개수는 0
System.out.println("-------------------");
hello("홍길동"); // 이 경우 names 배열의 개수는 1
System.out.println("-------------------");
hello("홍길동", "임꺽정", "유관순"); // 이 경우 names 배열의 개수는 3
System.out.println("-------------------");
// 가변 파라미터 자리에 배열을 직접 넣어도 됨
String[] arr = {"김구", "안중근", "윤봉길", "유관순"};
hello(arr);
System.out.println("-------------------");
// hello("홍길동", 20, "오호라"); // 다른 타입은 안됨 -> 컴파일 오류!
}
}
# 메서드 : 가변 파라미터
public class Exam0251 {
// 가변 파라미터에 배열을 넘길 경우
// => 가변 파라미터에 배열을 넘길 경우 그 배열을 그대로 받아 사용
static void hello(String... names) {
for (int i = 0; i < names.length; i++) {
names[i] += "^^";
System.out.printf("%s님 반갑습니다.\n", names[i]);
}
}
public static void main(String[] args) {
String[] arr = { "김구", "안중근", "윤봉길", "유관순" };
// 가변 파라미터에 배열을 넘길 경우
hello(arr);
System.out.println("-------------------");
for (String value : arr) {
System.out.println(value);
}
}
}
ㄴ 가변파라미터일 경우 배열을 넘겨도 됨
가변 파라미터 vs 배열 파라미터
# 메서드 : 가변 파라미터 vs 배열 파라미터
public class Exam0260 {
// 가변 파라미터
static void hello(String... names) {
for (int i = 0; i < names.length; i++) {
System.out.printf("%s님 반갑습니다.\n", names[i]);
}
}
// 배열 파라미터
static void hello2(String[] names) {
for (int i = 0; i < names.length; i++) {
System.out.printf("%s님 반갑습니다.\n", names[i]);
}
}
public static void main(String[] args) {
// 가변 파라미터의 메서드를 호출할 때는
// => 다음과 같이 낱개의 값을 여러 개 줄 수도 있고,
hello("홍길동", "임꺽정", "유관순");
// String[] temp = {"홍길동", "임꺽정", "유관순"};
// hello(temp);
System.out.println("-------------------");
// => 또는 다음과 같이 배열에 담아서 전달할 수도 있음
String[] arr = {"김구", "안중근", "윤봉길", "유관순"};
hello(arr);
System.out.println("-------------------");
// 배열 파라미터의 메서드를 호출할 때는 가변 파라미터와 달리 낱개의 값을 여러 개 줄 수 없음
// hello2("홍길동", "임꺽정", "유관순");
// System.out.println("-------------------");
// => 오직 배열에 담아서 전달해야 함
String[] arr2 = {"김구", "안중근", "윤봉길", "유관순"};
hello2(arr2);
System.out.println("-------------------");
}
}
가변 파라미터 => 여러 개 선언 불가
1) 가변 파라미터는 여러 개 선언할 수 없음
=> 아규먼트의 시작과 끝을 구분할 수 없음
예) m1("aaa", "bbb", "aaa@test.com", "bbb@test.com");
어느 값이 names 배열에 들어가고, 어느 값이 emails 배열에 들어가는가?
static void m1(String... names, String... emails) {} // 컴파일 오류!
static void m1(String[] names, String[] emails) {} // OK!
=> 중간에 다른 타입이 온다 하더라도 안됨
static void m1(String... names, int a, String... emails) {}// 컴파일 오류!
static void m1(String[] names, int a, String[] emails) {} // OK!
위의 메서드는 값을 구분할 수 있을 것 같은데?
=> 그냥 다음과 같이 호출하면 되는 것 아닌가?
예) m1("aaa", "bbb", 100, "ccc", "ddd", "eee");
=> 사람들은 쉽게 구분할 수 있음
그러나 컴파일러가 이런 상황을 구분하려면 굉장히 복잡해짐
=> 그래서 가변 파라미터라는 문법의 이점은 사용하되 너무 복잡한 사용법은 지양하기 위해서 사용 방법을 간단히 한 것
2) 가변 파라미터는 반드시 맨 끝에 와야 함
=> 아규먼트의 시작과 끝을 구분할 수 없음
예) m2("aaaa");
static void m2(String... names, String a) {} // 컴파일 오류!
static void m2(boolean b, String... names, int a) {} // 컴파일 오류!
# 메서드 : 가변 파라미터의 단점
public class Exam0271 {
static void m2(int a, String... names) {} // OK!
// 배열 파라미터는 여러 개 선언할 수 있음
static void x1(String[] names, String[] emails) {}
// 배열 파리미터는 순서에 상관 없음
static void x2(String[] names, int a) {}
public static void main(String[] args) {
// 컴파일 확인하라
}
}
// - 메서드에 가변 파라미터는 한 개만 사용할 수 있음
// - 가변 파라미터는 반드시 맨 뒤에 와야 함
// - 그 이유는 복잡한 사용을 막기 위해서
ㄴ Frames 은 메서드가 호출될 때마다 새 프레임이 생성됨
ㄴ 로컬 변수와 부분 결과를 보유하고 메서드 호출 및 반환에 참여함
메서드 호출과 JVM Stack 메모리
Test.java
public class Test {
public static void main(String[] args) { // args 도 local 변수
System.out.println(args);
int a = 100, b = 200;
swap(a, b);
System.out.printf("main() : %d, %d\n", a, b);
}
static void swap(int a, int b){
int temp = a;
a = b;
b = temp;
System.out.printf("swap() : %d, %d\n", a, b);
}
}
# 메서드 : call by value
public class Exam0310 {
static void swap(int a, int b) {
System.out.printf("swap(): a=%d, b=%d\n", a, b);
int temp = a;
a = b;
b = temp;
System.out.printf("swap(): a=%d, b=%d\n", a, b);
}
public static void main(String[] args) {
int a = 100;
int b = 200;
// swap() 호출할 때 a 변수의 값과 b 변수의 값을 넘김
// => 그래서 "call by value"라 부름
// => 비록 swap()에서 a와 b라는 이름의 변수가 있지만,
// 이 변수는 main()에 있는 변수와 다른 변수임
swap(a, b);
System.out.printf("main(): a=%d, b=%d\n", a, b);
}
}
// call by value
// => 아규먼트가 primitive data type인 경우, 메서드를 호출할 때 값을 넘김
// => 자바에서는 primitive data type에 대해서 메모리(변수) 주소를 넘기는 방법이 없음
ㄴ call by value => 값을 넘기는 것
ㄴ 로컬 변수는 JVM Stack 에 만들어지고, new 로 만들어지는 변수는 heap 에 저장됨
call by reference
# 메서드 : call by reference
public class Exam0320 {
static void swap(int[] arr) {
System.out.printf("swap(): arr[0]=%d, arr[1]=%d\n", arr[0], arr[1]);
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
System.out.printf("swap(): arr[0]=%d, arr[1]=%d\n", arr[0], arr[1]);
}
public static void main(String[] args) {
int[] arr = new int[] {100, 200};
swap(arr); // 배열 인스턴스(메모리)를 넘기는 것이 아님 => 주소를 넘기는 것
// => 그래서 "call by reference" 라 부름
System.out.printf("main(): arr[0]=%d, arr[1]=%d\n", arr[0], arr[1]);
}
}
ㄴ JVM Stack => 메서드의 로컬변수를 두는 영역
ㄴ Heap => new 라는 명령으로 만들어지는 변수를 두는 영역
객체와 call by reference
# 메서드 : call by reference II
public class Exam0330 {
// main()에서 만든 int a와 int b의 값을 바꾸고 싶다면,
// primitive data type 값을 직접 넘기지 말고
// 객체에 담아 넘겨라
static class MyObject {
// => class 는 메모리의 구조를 설계하는 문법
// => new 명령을 이용하여 변수를 생성할 수 있음
int a;
int b;
}
static void swap(MyObject ref) {
System.out.printf("swap(): a=%d, b=%d\n", ref.a, ref.b);
int temp = ref.a;
ref.a = ref.b;
ref.b = temp;
System.out.printf("swap(): a=%d, b=%d\n", ref.a, ref.b);
}
public static void main(String[] args) {
// MyObject 설계도에 따라 int a와 int b 메모리를 만듦
// 그리고 그 메모리(인스턴스=객체)의 주소를 ref 변수에 저장
MyObject ref = new MyObject();
ref.a = 100;
ref.b = 200;
// a, b 변수가 들어 있는 인스턴스(객체=메모리)의 주소를
// swap()에 넘김 => 그래서 "call by reference"인 것
swap(ref);
System.out.printf("main(): a=%d, b=%d\n", ref.a, ref.b);
}
}
ㄴ 레퍼런스 => 주소를 담는 변수
ㄴ MyObject 클래스에서 non-static 변수를 힙에 준비
ㄴ Heap에 저장되어있는 메모리는 연속되어있으며, 각 변수는 기본 값이 0으로 초기화 되어있음
ㄴ new 명령으로 만들어진 변수의 연속된 메모리들을 MyObject 의 Instance 라고 하며, 이는 더 큰 의미로 말하면 MyObject 의 객체(Object) 라고 함
객체 생성과 리턴
# 메서드 : 레퍼런스를 리턴하기
public class Exam0340 {
// swap()에서 만든 int a와 int b의 값을 main()에서 사용하기
// primitive data type 값을 객체에 담아 넘겨라
static class MyObject {
int a;
int b;
}
static MyObject swap(int a, int b) {
MyObject ref = new MyObject();
ref.a = b;
ref.b = a;
return ref;
}
public static void main(String[] args) {
int a = 100;
int b = 200;
MyObject ref = swap(a, b);
System.out.printf("main(): ref.a=%d, ref.b=%d\n", ref.a, ref.b);
}
}
메서드 : JVM 메모리
public class Exam0410 {
static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.printf("swap(): a=%d, b=%d\n", a, b);
}
public static void main(String[] args) {
int a = 100;
int b = 200;
swap(a, b);
System.out.printf("main(): a=%d, b=%d\n", a, b);
}
}
// 실행 순서와 메모리
// 1) java -classpath bin com.eomcs.lang.ex07.Exam0410
// => JVM은 클래스 정보를 Method Area 영역에 로드
// 2) main() 호출
// => JVM Stack 영역에 main() 메서드가 사용할 로컬 변수를 준비
// 3) swap() 호출
// => JVM Stack 영역에 swap() 메서드가 사용할 로컬 변수를 준비
// 4) swap() 실행 완료
// => JVM Stack 영역에 있던 swap()이 사용한 메모리를 제거
// 5) main() 실행 완료
// => JVM Stack 영역에 있던 main()이 사용한 메모리를 제거
// 6) JVM 실행 종료
// => OS가 JVM에게 사용하라고 빌려줬던 모든 메모리를 회수함
// JVM이 메모리를 다루는 방법
// - 크게 다음 세가지 영역으로 나눠 관리
// 1) Method Area
// - 클래스 명령 코드를 둠
// - static 변수를 둠
// 2) Heap
// - new 명령으로 만든 메모리(인스턴스=객체)를 둠
// - Garbage Collector(GC)가 관리하는 영역임
// 3) JVM Stack
// - 스레드 별로 JVM Stack 메모리를 따로 관리
// - 메서드의 로컬 변수를 둠
// - 각 메서드마다 프레임 단위로 관리함
// - 메서드 호출이 끝나면 그 메서드가 사용한 프레임 메모리가 제거됨
// - 이렇게 메서드가 호출될 때 로컬 변수가 준비되고 맨마지막에 호출한 메서드가 먼저 삭제된다고 해서
// "스택(stack)" 메모리라 부름
// 스택? 접시 쌓는 것을 생각하라
// - 스택 방식을 "Last In First Out(LIFO;후입선출, FILO;선입후출)"라 부름
// JVM이 종료하면 JVM이 사용했던 모든 메모리를 OS가 회수함
메서드 : Heap 메모리 영역
public class Exam0420 {
static int[] getArray() {
int[] arr = new int[] {100, 200, 300};
// => int 배열 주소를 담을 arr 변수를 JVM Stack 영역에 준비
// => 100, 200, 300 값을 담은 배열을 Heap 영역에 준비
// => Heap 영역에 준비한 배열 메모리의 주소를 JVM Stack 메모리에 있는 arr 변수에 넣음
return arr;
}
public static void main(String[] args) {
int[] arr;
arr = getArray();
System.out.println(arr[1]); // 200
}
}
// 1) main() 호출
// => JVM Stack: args, arr 변수 생성
// 2) getArray() 호출
// => JVM Stack: arr 변수 생성
// => Heap: new int[] 배열 생성
// 3) getArray() 호출 끝
// => JVM Stack: getArray() 관련 메모리(arr 변수) 제거
// => new int[] 배열 주소 리턴
// 4) main() 호출 끝
// => JVM Stack: main() 관련 메모리 제거
// 5) JVM 종료
// => JVM이 사용한 모든 메모리(Method Area, JVM Stack, Heap 등)를 OS 반납
배열의 생성은 어디서 하는 것이 좋은가?
public class Exam0421 {
public static void main(String[] args) throws Exception {
int[] moneys = new int[] {100, 200, 300};
float[] totals = new float[moneys.length];
// 호출하는 쪽에서 결과를 담을 배열을 주는 경우
compute(moneys, totals, 0.0089f);
for (int i = 0; i < moneys.length; i++) {
System.out.printf("%d => %.1f\n", moneys[i], totals[i]);
}
System.out.println("---------------------");
float[] result;
// 메서드 쪽에서 결과를 담을 배열을 만들어 리턴하는 경우
result = compute2(moneys, 0.0089f);
for (int i = 0; i < moneys.length; i++) {
System.out.printf("%d => %.1f\n", moneys[i], result[i]);
}
}
static void compute(int[] moneys, float[] totals, float interest) {
for (int i = 0; i < moneys.length; i++) {
totals[i] = moneys[i] + (moneys[i] * interest);
}
}
static float[] compute2(int[] moneys, float interest) {
float[] totals = new float[moneys.length];
for (int i = 0; i < moneys.length; i++) {
totals[i] = moneys[i] + (moneys[i] * interest);
}
return totals;
}
}
메서드 : 인스턴스와 Heap 메모리 영역
public class Exam0430 {
// Heap 메모리에 어떤 변수를 만들어야 하는지 적어 놓은 설계도
// => 나중에 new 명령을 사용하여 메모리를 만들라고 하면,
// MyObject에 적어 놓은 변수를 Heap 영역에 생성하라는 뜻
static class MyObject {
int a;
int b;
}
static MyObject getMyObject() {
MyObject ref = new MyObject(); // MyObject에 선언된대로 변수를 Heap에 만들어라
ref.a = 100;
ref.b = 200;
return ref;
}
public static void main(String[] args) {
MyObject ref;
ref = getMyObject();
System.out.println(ref.a);
System.out.println(ref.b);
}
}
// 1) main() 호출
// => JVM Stack: args, ref 변수 생성
// 2) getMyObject() 호출
// => JVM Stack: ref 변수 생성
// => Method Area: MyObject 클래스를 로딩
// => Heap: MyObject 설계도에 따라 인스턴스 생성
// 3) getMyObject() 호출 끝
// => JVM Stack: getMyObject() 관련 메모리(ref 변수) 제거
// => MyObject의 인스턴스의 주소 리턴
// 4) main() 호출 끝
// => JVM Stack: main() 관련 메모리 제거
// 5) JVM 종료
// => JVM이 사용한 모든 메모리(Method Area, JVM Stack, Heap 등)를 OS 반납
메서드 : 스택 메모리 응용
public class Exam0440 {
static int m1(int value) {
int r1 = m2(value);
int r2 = m3(value);
return r1 + r2;
}
static int m2(int value) {
return value + 100;
}
static int m3(int value) {
return value + 200;
}
public static void main(String[] args) {
int r = m1(5);
System.out.println(r);
}
}
// JVM Stack 메모리의 사용
// 0) 시작
// 1) main()
// 2) main() => m1()
// 3) main() => m1() => m2()
// 4) main() => m1()
// 5) main() => m1() => m3()
// 6) main() => m1()
// 7) main()
// 8) 종료
# 메서드 : main() 메서드
public class Exam0510 {
// JVM이 클래스를 실행할 때 main() 메서드를 호출함
// 메인 메서드는 반드시 다음과 같은 메서드 시그너처(함수 프로토타입)를 가져야 함
// public static void main(String[] 변수명)
//
public static void main(String[] 변수명은상관없음) {
System.out.println("Hello!");
}
}
main()의 아규먼트
# 메서드 : main() 메서드 - 프로그램 아규먼트
public class Exam0520 {
// 프로그램 아규먼트
// - jvm을 실행할 때 프로그램에 전달하는 값
// - 예)
// > java -cp bin/main com.eomcs.lang.ex07.Exam0520 aaa bbb cccc
// aaa bbb cccc 가 프로그램 아규먼트임
//
public static void main(String[] args) {
// 프로그램 아규먼트는 스트링 배열에 담겨서 main()를 호출할 때 넘어옴
// 프로그램 아규먼트는 공백을 기준으로 문자열을 잘라서 배열을 만듦
// 아규먼트가 없으면 빈 배열이 넘어옴
for (String value : args) {
System.out.printf("[%s]\n", value);
}
System.out.println("종료!");
}
}
메서드 : main() 메서드 - 프로그램 아규먼트 응용 I
public class Exam0530 {
public static void main(String[] args) {
// 합계를 출력하는 프로그램을 작성하라
// $ java -cp ./bin/main com.eomcs.lang.ex07.Exam0530 200 43 56 // 문자열
int sum = 0;
for (String arg : args)
sum += Integer.parseInt(arg);
System.out.printf("합계: %d\n", sum);
}
}
// # 프로그램 아규먼트(arguments)
// - 프로그램을 실행할 때 넘겨주는 값
// - 어떻게 아규먼트를 넘기는가?
// $ java 클래스명 값1 값2 값3
// - 아규먼트는 공백으로 구분
// - JVM은 아규먼트의 개수만큼 문자열 배열을 만들어 저장
// - 아규먼트가 없으면 빈 배열을 만듦
// - 그 후 main()을 호출할 때 그 배열의 주소를 넘겨줌
메서드 : main() 메서드 - 프로그램 아규먼트 응용 II
public class Exam0540 {
public static void main(String[] args) {
// 학생의 이름과 국영수 점수를 입력 받아 총점과 평균을 출력하라
// $ java -cp ./bin/main com.eomcs.lang.ex07.Exam0540 홍길동 100 100 90
// 이름: 홍길동
// 총점: 290
// 평균: 96.9
if (args.length < 4) {
System.out.println(
"실행 형식: java -cp ./bin/main com.eomcs.lang.ex07.Exam0540 이름 국어점수 영어점수 수학점수");
return; // <-- JVM 종료
}
int sum = 0;
for (int i = 1; i < args.length; i++)
sum += Integer.parseInt(args[i]);
System.out.printf("이름: %s\n", args[0]);
System.out.printf("총점: %d\n", sum);
System.out.printf("평균: %.1f\n", sum / 3f);
}
}
실습
import java.util.Scanner;
public class App {
static Scanner scanner = new Scanner(System.in);
static final int MAX_SIZE = 100;
static int[] no = new int[MAX_SIZE];
static String[] name = new String[MAX_SIZE];
static String[] email = new String[MAX_SIZE];
static String[] password = new String[MAX_SIZE];
static char[] gender = new char[MAX_SIZE];
static int userId = 1;
static int length = 0;
public static void main(String[] args) {
printTitle();
while (length < MAX_SIZE) {
inputMember();
if (!promptContinue()) {
break;
}
}
printMembers();
scanner.close();
}
static void printTitle() {
System.out.println("나의 목록 관리 시스템");
System.out.println("----------------------------------");
}
static void inputMember() {
System.out.print("이름? ");
name[length] = scanner.next();
System.out.print("이메일? ");
email[length] = scanner.next();
System.out.print("암호? ");
password[length] = scanner.next();
loop: while (true) {
System.out.println("성별: ");
System.out.println(" 1. 남자");
System.out.println(" 2. 여자");
System.out.print("> ");
String menuNo = scanner.next();
scanner.nextLine(); // 입력 값(token)을 읽고 난 후에 남아 있는 줄바꿈 코드를 제거한다.
switch (menuNo) {
case "1":
gender[length] = 'M';
break loop;
case "2":
gender[length] = 'W';
break loop;
default:
System.out.println("무효한 번호입니다.");
}
}
no[length] = userId++;
length++;
}
static boolean promptContinue() {
System.out.print("계속 하시겠습니까?(Y/n) ");
String response = scanner.nextLine();
if (!response.equals("") && !response.equalsIgnoreCase("Y")) {
return false;
}
return true;
}
static void printMembers() {
System.out.println("---------------------------------------");
System.out.println("번호, 이름, 이메일, 성별");
System.out.println("---------------------------------------");
for (int i = 0; i < length; i++) {
System.out.printf("%d, %s, %s, %c\n", no[i], name[i], email[i], gender[i]);
}
}
}
ㄴ static 변수 => static 메서드끼리 공유하여 사용 가능
'네이버클라우드 > JAVA 웹 프로그래밍' 카테고리의 다른 글
JAVA 11일차 (2023-06-07) 자바 기초 DAY9_개인프로젝트 - 마트 물품 관리 시스템 (0) | 2023.06.07 |
---|---|
JAVA 10일차 (2023-06-05) 자바 기초 DAY8 (0) | 2023.06.06 |
JAVA 7일차 (2023-05-31) 자바 기초 DAY5 (0) | 2023.05.31 |
JAVA 6일차 (2023-05-30) 자바 기초 DAY4 (0) | 2023.05.30 |
JAVA 5일차 (2023-05-26) 자바 기초 DAY3 (0) | 2023.05.26 |