언제까지나 계속 변수를 직접선언해서 값을 출력할 수 없다.
사용자로 부터 값을 입력받아서 출력해보자.
- Scanner을 쓰기 위해서 객체화를 해주었더니 오류가 발생했다.
- 해당 오류는 아래와 같은 오류다.
'div(int, int)' in 'dev.jwkim.Calc' cannot be applied to '()'
: 메서드가 요구하는 매개변수의 구조에 맞게 전달인자를 전달해라 라는 오류.
command + p
를 해보면 어려운 것들이 막 보인다.
=> 값을 입력받을 때는 약속인 것처럼!
System.in
을 쓰면된다.
출력하기 위해서는 System.out
을 이용하였으니 입력을 받을 때 System.in
을 쓰는 것으로 이해하자.
a = scanner.nextInt();
: scanner 객체가 가지고 있는 nextInt() 사용한다.- 17과 3을 입력하였고 연산이 된 결과도 출력이 되었다.
scanner.nextLine();
: 문자열 입력을 받을 때 사용.
next() : 공백단위로 끊어 문자열 입력 받음
nextLine() : 개행 단위로 끊어 문자열 입력 받음 : enter로 친 것으로 인식한다.
- 개행을 원치 않기 때문에 next()을 사용하였다.
nextInt( )
: 17 + enter
=> 17\n
이 된 것이다.
17\n
: 17 | sacnner "\n"
nextLine()
을 사용하고 싶다면 scanner가 가지고 있는\n
을 버리면 된다.
- if 두개보다는 else if를 이용해 줄이자. / switch문으로 써도 괜찮다. (문자열 비교는 if문에서 equals를 쓰기 힘들기에 switch도 쓴다.)
사용자가 종료를 한다는 의사표현을 하기 전까지 반복이 되었으면 좋곘다.
기약없는 반복을 위해서는 while 사용한다.
- return을 사용하게 되면 도달할 수 없는 구문이다 라는 오류 발생한다. 해당오류는 아래에 처리하는 방법에 대해 적어놓았다.
- 대문자로 적어도 가능하게 한다. (equalsIgnoreCase)
<String>.toLowerCase().equals(x) 와 <String>.equalsIgnoreCase(x) 는 같다.
사용자가 입력한 문자열 홀수 / 짝수 구분 출력
POP(절차지향프로그래밍 Procedural-Oriented Programming) 에서 OOP(객체지향프로그래밍 Object-Oriented Programming)으로
Integer.parseInt(s)
: 전달된 문자열 객체인 s를 정수로 반환하여 변환해준다.
- Interger 클래스에 바로 접근을 했으니 parseInt는 정적인 메서드며
parseInt의 접근제한자는 Private이 아닐 것이다 라는 예측이 된다.
ex)Long.parseLong(s);
\t
: tab-> 사용자가 입력한 문자열 홀수 / 짝수 구분 출력
- 입력받은 command를 형변환하기 위해서 parseInt를 사용한다. (반환타입 int다.)
int number = Integer.parseInt(command);
에서 걸리게 된다.
help -> 정수? : help를 입력했는데 문자열을 숫자로 바꿀 수 없으니 오류가 뜨는 것이다. ❗️ 그럴 때 예외를 처리해주어야 한다!
: 엄밀히 얘기하면 오류가 아니다. 예외라고 해야한다.
주의 : 처리되지 않은 예외(Unhandled Exception)가 발생하면 그 자리에서 프로그램의 실행이 즉시 종료. 예측가능한 경우에는 발생 가능한 예외에 대해 처리(Handle) 해줘야한다.
↓ 예외 ↓ 어떤 예외? ↓ 예외 메세지 Exception in thread "main" java.lang.NumberFormatException: For input string: "help" → at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) → at java.lang.Integer.parseInt(Integer.java:580) → at java.lang.Integer.parseInt(Integer.java:615) → at Ex.main(Ex.java:18) → 가장 아래에 있는 스택이 가장 근원(Root Cause, 최초 호출부) ↑ 스택 트레이스 ↑ 문제가 발생한 파일 : 줄 번호 (Stack Trace)
- 밑으로 가면 갈수록 오류의 근원지가 나온다.
main -> Integer.parseInt -> Integer.parseInt -> forInputString을 하다가 NumberFormatException이 발생했다.
- 메서드가 총 4개가 있는데 우리가 직접 만든 코드는 main 밖에 없다.
- 내가 짠 코드는 파란색이고 다른 사람이 짠 코드는 회색이다.
- 즉 회색에는 문제가 없고
Ex.java:18
에서 문제가 발생했다는 것이다.
문제를 해결하기 위해서는 새로운 개념이 나온다.
help를 입력했을 때는 command를 parseInt 할 필요가 없기 때문에 계속 진행시켜주면 된다. 그럴 때 사용하는 것이 countinue이다.
: 현재 반복문의 진행중인 반복에 대해 중단하고 다음 반복을 시작함.
- continue가 걸리면 밑으로 내려가는 것이 아니라 다시 while문으로 간다. (오류발생하지 않음)
return : 메서드 빠져나감 break : 반복문 빠져나감 +) continue을 하더라도 i++이 있다면 증감은 된다.
exit / help
에 대한 예외처리는 break와 continue로 해줬는데 exit / help가 아닌 다른 문자열을 입력을 하게 되면 NumberFormatException이 발생 할 것이다. (ex ) Integer.parseInt("abc");
)
사용자 입력값이 있을 경우에는 무조건 예외를 처리해줘야한다.
try { 예외가 발생할 가능성이 있는 구문을 담는 구현부 } catch (처리를 하고자하는 예외 선언) { //처리를 하고자하는 예외 타입 예외가 발생했다면 실행할 구현부 } catch (처리를 하고자하는 예외 선언) { 예외가 발생했다면 실행할 구현부 } finally { try가 실행되었든 catch가 실행되었든 Try-Catch 전체 구문을 빠져나가기 전에 반드시 실행되어야하는 내용 }
- (대부분의 경우) catch문은 1개 이상 작성한다.
- catch문을 2개 이상 작성하는 경우 각 catch가 가지는 예외의 타입은 달라야한다.
- 타입과 이름를 가지는 변수형태이다. (변수선언과 구조가 같다.)
catch에 들어왔다는 것은 try 하다가 NumberFormatException가 발생해서 들어온 것이다.
- catch 구현부에서 NumberFormatException이 발생하면 sout으로 알려주자.
- try의 구현부는 예외가 발생하자 마자 중단이 되고 바로 catch 구현부로 내려간다.
try에 대한 예외처리는 되었지만 catch에 대한 예외처리는 되지 않았다.
이게 무슨 말인지 예시를 통해 이해해보자.
- catch 구현부에서 NumberFormatException이 뜨도록 해보았다.
- catch에 대한 예외처리는 되어있지 않아서 오류가 발생한 것을 확인 할 수 있다.
❗️ 해당 try는 NumberFormatException에 대한 예외처리만 해줄뿐 다른 예외에 대해서는 처리해 주지 않는다. 처리를 해주기 위해서는 catch를 추가해서 넣으면 된다.
모든 예외들의 부모인 Exception으로 다 처리를 할 수 있긴 한데 좋은 방법은 아니다.
NullpointException 을 발생시켜보자
String s = null;
s.toString();
STACK 영역 s (변수명)
null (값)
0x00 (주소값)
어떠한 참조(레퍼런스) 타입이 Stack 영역에서 (값자리에) Heap 영역의 주소값을 가져야하는데 Heap영역에 대해서 가르킬 내용이 없기 때문에 null이 들어가게 되어 NullpointException이 발생한다. (참조타입만 가능하다.)
int n = null; : 불가능! 기초타입은 null을 가질 수 없다.
Integer i = null; : 가능! 클래스이며 참조타입이기 때문에 null일 수는 있는 것이다.
Heap에 가르키는 영역이 없는 null을 가진 래퍼런스 타입에 .
을 하게 되면 무조건 NullpointException이 발생한다.
printStackTrace()
메서드를 호출해주면 예외처리가 되어도 정상 실행 중이게 된다.- 이 메서드 없이 예외 처리가 되면 아무런 현상도 발생하지 않고 다음으로 넘어가기 때문에 확인을 할 수가 없다.
그래서 어떠한 오류가 발생했는지 확인하고 싶다면 이 메서드를 사용해주면 된다.
- catch문을 빠져나가서 적어도 sout 언제나 똑같이 출력이 될텐데 finally를 왜 사용하는 걸까?
- 확인을 위해 새로운
getStringToPrint
메서드안에 try catch를 똑같이 적고 catch에 return을 해주었다.System.out.println("분석 끝");
이게 안된다.
Unreachable statement
: 위 실행 결과론 여기까지 도달할 수 없다. 라는 의미의 오류가 발생한다.
만약 NullPointException이 발생하면 try catch에서 처리를 못해주는데 왜 도달할 수 없다고 (Unreachable statement) 할까?
먼저 try안의 모든 내용이 정상적으로 실행이 되었다면 도달하지 않는다.
try안 if에서 true일떄나 false일 때 결과적으로는 return을 하기 때문에 실행이 끝나버린다. 그리고 NumberFormatException이 발생하더라도 return이 발생하기 때문에 밑으로 내려갈 일이 없다.
그렇다면 그외의 모든 예외가 발생하면 어떻게 되는가?
처리되지 않은 예외가 발생하면 프로그램이 종료가 된다.
NumberFormatException이 아닌 다른 예외가 발생한다면 프로그램이 종료가 됨으로 도달할 일이 없는 것이다.
분석 끝을 출력하고 싶다면 예외가 몇개가 됬든 다 붙여주게 되면 하드코딩이 되는 것이다. 그때 사용하는 것이 finally이다.
반환타입을 가지는 모든 메서드는 어떠한 경우에라도 return을 해줘야한다.
그런데 finally은 retrun이 없는데 정상적으로 작동을 한다.
finally는 try가 실행되었든 catch가 실행되었든 누군가가 실행되어서 실행을 하는 애가 아니라 언제나 실행을 보장해준다.
finally가 정확히 언제 실행이 되는지에 대해 알아보자.
메서드 실행
command에 5라는 값이 들어옴.
nummber = 5;
if문 : false;
← return을 해주기 전에 finally 실행
return 5는 홀수다.
return을 만나게 되면 메서드를 종료하려고 할 것이다.
메서드가 종료가 되려면? stack이 쌓여있기 때문에 순서대로 빠져나간다.
else -> try / catch -> getStringToPrint
로 순서로 빠져나가게 되는데 그러던 중 try를 빠져나가기전에 finally가 있으면 finally를 실행시키고 빠져나가게 되어 실행이 무조건 되는 것이다.
- 메인에서 getStringToPring 메서드를 호출해보니
- 분석 끝이 먼저 나왔다.
즉, 메인메서드에서 실제로 값을 return해 주기전에 finally를 실행한 것이다.
처리해주지 않는 에외가 try안에서 발생하더라도 finally는 꼭 꼭 실행시킨다.
- 메서드를 바로 뺴서 사용함으로써 메인에서 한줄로 줄게 되었다.
지금까지 쓴 것은 객체 지향이라기(객체를 쓰지 않았다.) 보다 절차 지향에 가깝지만 역할에 따라 메서드 분리는 한 것이다.
메서드 하나 하나는 최소한의 역할을 할 수 있도록 구성을 해야한다.
- help에 대한 내용도 아래에 메서드로 작성해서 메서드 호출을 했다.
- continue는 반복문에 대해서 밑에 나오는 내용을 무시하고 다음 반복을 시작하겠다는 의미인데 printHelp메서드로 빼버리면 반복문이 아니기 때문에 continue은 반복문안에 두어야한다.
실행점은 단 하나밖에 없기 때문에 printHelp를 호출하는 순간 printHelp메서드로 내려간다.
메서드가 실행되는 동안에는 printHelp의 실행이 끝날 때까지는 더 이상 진행하지 않는다.
Google search tip
"x" : x라는 단어 반드시 포함
stie:domain.name : 'domain.name'의 결과만 출력
-stie:domain.name : 'domain.name'의 결과만 제외
intitle:x : 제목에 x가 들어가있어야 결과에 포함
-intitle:x : 제목에 x가 들어가있어야 결과 제외
after:y : y년도 이후 글만 검색
before:y : y년도 이전 글만 검색