예외처리의 종류는?

 

- 오라클 문서에서는 예외(Exception)을 3가지로 구분하고있다. 

1) Checked Exception

 : 컴파일러가 예외처리를 제대로 했는지, 확인하는 예외들을 의미한다. 다시 말하여, 프로그래머인 우리가 catch문에서 예외를 처리해주든, 혹은 throws를 통하여 처리해달라고 요청을 하여 예외를 처리할 수 있게끔해야 한다. 

 

2) Error

 : 프로그램 외부에서 발생하는 예외를 의미한다. 오라클 문서에서는 Error 를 예외로 분류하고 있으나, Error.java의 주석을 읽어보면 비정상적인 상황으로 기인한 Runtime Exception으로 간주되어 진다. 

 

3) Unchecked Exception(=Runtime Exception) 

 : 프로그램 내부에서 발생하는 예외이나, 컴파일러가 확인하지 않는 예외들을 의미한다. 이는 보통 실행시에 발생하는 예외들이다. 

 

현상만 놓고보자면, Checked Exception 이 발생되는 경우에는 컴파일이 안될 것이며, 그외는 런타임에서 발생한다

 

예외처리는 어떻게하는가? 

 

- 예외처리는 try-catch-(finally) 로 처리하면 된다. 

public void tryCatchFinally() {
        int[] array = new int[5];

        try {
            // 처리해야하는 코드 - (1)
            System.out.println(array[5]);

        }

        catch(NullPointerException e){
            // 예외가 잡혔을 때 처리할 코드 - (2)
            // 부모클래스를 먼저 catch 문의 조건으로 잡으면 컴파일이 되지 않음.  - (2.1)
                // java: exception java.lang.NullPointerException has already been caught
            // NullPointerException 이 아닌경우 해당 catch 문은 수행되지 않음 - (2.2)
            e.printStackTrace();

        }
        catch (Exception e) {
            // 예외가 잡혔을 때 처리할 코드 - (2)
            // Exception은 Throwable 을 상속받음 - (2.3)

            // printStackTrace - (2.4)
            e.printStackTrace();
            // getMessage() - (2.5)
            System.out.println(e.getMessage());
            // getCause - (2.6)
            System.out.println(e.getCause());

        }
        finally {
            // 예외 발생과는 상관없이 언제나 실행되는 코드, 생략가능 - (3)
            System.out.println("Finally Block is called");
        }
    }

위의 코드를 보면 

(1) : try 블록 내에서는 예외가 발생할 수 있는 코드를 작성한다. 

(2) : catch 블록 내에서는 해당하는 예외를 처리하는데, 

 (2.1) Exception 을 catch하는 경우 더 포괄적인 부분을 작성한 경우 컴파일이 되지 않는다. 이 예제 기준으로 만약  Throwable 혹은 Exception을 먼저 catch하게끔 작성한 경우 NullPointerException 예외는 검사되지 않는다. 

고로 Exception은 상세한 내용부터 catch하여 포괄적인 내용으로 catch 되어야 한다. 

 (2.2) ArrayIndexOutOfBoundsException 이 발생했는데, 다른 NullPointerException 같은걸로 잡으려면 안잡힌다. 

그렇지만 부모 클래스인 IndexOutOfBoundsException 으로 catch는 가능하다. 

 (2.4 ~ 2.6) 

  - printStackTrace :  예외가 발생했을 때의 호출스택을 보여준다

  - getMeassage : 예외가 왜 발생했는지를 알려준다

  - getCause : 예외의 종류 + 예외가 왜 발생했는지를 알려준다

 

(3) : finally 블록 내에서는 예외 발생과 상관없이 언제나 실행되는 코드들이며, 생략가능한 블록이다. 오라클 공식문서에서는 try block이 exit하기 전에 언제나 수행된다고 설명되어있다(다음 예외처리의 동작흐름에 대해 별도로 설명할 예정)

 

즉, 특별히 return 을 하지 않는 이상 try -> finally 또는 try -> 오류발생(오류 발생지점 이후 try문은 수행되지 않음) -> catch -> finally 이다. 다만 특이한 점이 있어 따로 흐름에 대해서는 다음 내용에 정리하겠다. 

 

try-with-resources ! 

try-with-resources는 자원해제(close()) 처리를 까먹는 경우를 예방하기 위함이다. 이렇게 사용하기 위해서는 

java.lang.AutoCloseable(Closeable포함) 인터페이스를 구현한 것들에 한해 사용이 가능하다. 

 

아래의 코드에서 out.write(b) 가 실패하는 경우, out 자원을 해제하기 위해 finally 블록에서 자원해제를 해주는 코드가 필여요하다. (애초에 try 블록에서 안하면 되지만 어쨋든 코드의 중복을 얘기하고 싶었다) 

private void writeByte(byte[] b) {
        // get current path
        String path = System.getProperty("user.dir");

        FileOutputStream out = null;
        File file = new File(path.toString()+"/writeBeforeJDK1_7.txt");
        try{
            out = new FileOutputStream(file, true);
            out.write(b); // Write byte[] in here - (1)
            out.close();  // Close File in here

        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        finally {
            // Check whether outputStream is closed or not. - (2)
            if (out !=null){
                try{
                    out.close();
                }
                catch (Exception e ){
                    // Handle Exception
                    e.printStackTrace();
                }
            }
        }

    }

위의 코드를 try-with-resources 를 활용하여 작성하면 이렇게 많은 생산성을 확보하고 실수가능성을 줄일 수 있다.

 

private void writeByteTryWithResources(byte[] toWrite){
        // get current path
        String path = System.getProperty("user.dir");

        File file = new File(path.toString()+"/writeAfterJDK1_7.txt");
        try(FileOutputStream out = new FileOutputStream(file, true)){
            out.write(toWrite);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }

 

 

예외처리는 어떻게 흘러가는가? 

위에서 소결론을 내렸으나, return 에 따라 특이한 부분이 존재하여 별도로 정리해보고자 한다. 

아래는 실제 return을 catch문과 finally문에 작성해본 코드이다. 

 public String noReturnInCatchAndFinally() {
        System.out.println("noReturnInCatchAndFinally method is Called!!");
        int[] array = new int[5];

        try {
            System.out.println("try Block is called");
            System.out.println(array[5]);
        }
        catch (Exception e) {
            System.out.println("catch Block is called");
            e.printStackTrace();
        }

        finally {
            System.out.println("finally Block is called");
        }
        return "endOfControl";

    }

    public String returnInCatch_noReturnInFinally() {
        System.out.println("returnInCatch_noReturnInFinally method is called!!");
        int[] array = new int[5];

        try {
            System.out.println("try Block is called");
            System.out.println(array[5]);
        }
        catch (Exception e) {
            System.out.println("catch Block is called");
            return "catch";
        }
        finally {
            System.out.println("finally Block is called");
        }
        // (1) try 문으로 이어짐.
        return "OutSideOffinally";
    }

    public String noReturnInCatch_returnInFinally() {
        System.out.println("noReturnInCatch_returnInFinally method is called!!");
        int[] array = new int[5];

        try {
            System.out.println("try Block is called");
            System.out.println(array[5]);
            return "try";
        }
        catch (Exception e) {
            System.out.println("catch Block is called");
        }
        finally {
            System.out.println("finally Block is called");
            // (2) try 문의 return은 사라짐
            return "OutSideOffinally";
        }
    }

이 코드를 디컴파일해보면, 

(1) catch 블록에 return 이 있는 경우 

 :별도의 String 변수를 생성하여, Exception이 발생하는 경우 이를 return 함 

(2) finally 블록에 return 이 있는 경우 

 : finally에만 return 이 남게됨. 이는 try, catch에 아무리 return 을 넣어도 결과적으로 finally에서 return을 통해 exit를 하기 때문에 컴파일러가 컴파일 시, try 및 catch 블록에 return 을 만들지 않음!!! 

 

이를 통해 얻는 교훈은, finally 블록안에는 절대 return 예약어를 사용하지 말것!!!!

 

 

예외는 어떻게 발생시키는가(+throws)?

// Exception handling by try-catch - (1)
    public void howToThrowExceptionWithTryCatch() {
        try {
            throw new Exception("예외 발생!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
// Exception handling with throws - (2)
public void howToThrowExceptionWithOutTryCatch() throws Exception {
    throw new Exception("예외 발생!");
}

// Throw Runtime Exception - (3)
public void throwUncheckedException() {
    throw new RuntimeException("Unchecked Exception!");
}

 (1) : throw 예약어를 통해 예외를 발생시키되, try-catch로 예외를 처리를 해주는 경우

 (2) : 메소드 시그니쳐에 throws 를 추가하여 메소드를 호출한 쪽에서 예외를 처리하게끔 요청하는 경우 

-> 호출한 쪽에서 예외를 처리(try-catch 혹은 throws)를 해야하는데, throws로 처리하는 경우, 프로그램의 시작점인 main메소드에서도 throws를 하는 경우 프로그램은 종료. 

 (3) : 컴파일러가 위의 1번과 2번 케이스처럼 예외처리를 요청하지 않음. 용어그대로, Unchecked Exception이기 때문!

 

 

커스텀한 예외는 어떻게 만드는가?

// extends Exception or RuntimeException
public class CustomException extends Exception {

    public CustomException() {
        super();
    }

    public CustomException(String message) {
        super(message);
    }

    public CustomException(String message, Throwable cause) {
        super(message, cause);
    }

    public static void main(String[] args) throws Exception {
        throw new CustomException("Custom Error", new Throwable("Error happens with..."));
    }
}

Exception 또는 RuntimeException을 상속받아서, message, cause 등을 다 받을 수 있게끔 처리하면 된다. 

 

'가치코딩 [Java] > 기초문법' 카테고리의 다른 글

[Java 문법] enum  (0) 2021.02.04
[Java 문법] 패키지  (1) 2021.02.03
[Java 문법] 클래스  (0) 2020.12.18
[Java 문법] 선택문 및 반복문  (0) 2020.12.08
[Java 문법] 자바 연산자  (0) 2020.11.28

[클래스]

클래스란 무엇인가? 

클래스란, 개체를 생성하기 위한 틀을 의미한다. 개체는 클래스를 통하여 생성된다. 

클래스는 동작과 상태를 갖고 있는데, 이는 상태와 동작이 분리된 과거의 절차지향 프로그래밍과는 다른 시각에서 접근하였다. 정해진 절차를 수행하는 것과 같은 알고리즘과는 다르게 프로그래머의 주관이 많이 개입되는 영역이다. 개체를 어떻게 보는지에 따라 같은 목표를 달성하기 위한 동작과 상태를 다르게 작성할 수 있다. 

 

클래스는 어떻게 정의하는가? 

1) 상태(멤버변수)는 무엇이며 어떻게 정의하는가? 

멤버변수는 개체의 상태를 나타내며 정의방법은 아래와 같다.

private int age; // <접근제어자> <자료형> <변수명> 으로 구성

 

2) 동작(메소드)는 무엇이며 어떻게 정의하는가?

메소드는 개체의 동작을 나타내며 정의방법은 아래와 같다.

public int getAge(); // <접근제어자> <반환할 자료형> <메소드명> (파라미터) 으로 구성

 

3) 생성자는 무엇이며 어떻게 정의하는가? 

생성자는 개체를 생성하는 역할을 수행하며 정의 방법은 아래와 같다. 

// 클래스 정의 생략
public Person(int age, String name){ //<접근제어자> <클래스명> (파라미터) {초기화코드} 로 구성
  this.age = age;
  this.name = name;
}

이때, 접근제어자는 개체 생성시 차이가 존재하며 private인 경우 디자인패턴에서, 싱글턴 패턴이라는 형식으로 사용되기도 한다. 

접근제어자 별 차이는 아래와 같다. 

// 외부에서 개체 생성이 불가능
private Person(){
  
}

// 동일 패키지내 or 해당 클래스를 상속받은 경우에 개체 생성가능
protected Person(int age, String name){
  this.age = age;
  this.name = name;
}

// 동일 패키지에서 개체 생성가능
Person(String name){
  this.name = name;
}

// 어디에서나 개체 생성가능
public Person(int age){
  this.age = age;
}

 

4) this 키워드는 무엇인가?

this 예약어는 보통 아래와 같이 사용하는는데 그 목적은 컴파일러 입장에서 생성자에 의해  shadowed 된 멤버변수와 지역변수를 구분하기 위해 주로 사용한다고 한다. 위의 설명은 설명일뿐 실제로 가장 중요한 것은 개체 그 자신이라는 것이다. 위의 링크에서는 this()도 설명하는데, 이 경우 다른 생성자를 호출하기 위한 용도로 사용된다.

public Person(int age){
  this.age = age;
  System.out.println(this); // 개체의 인스턴스 주소를 출력함을 확인할 수 있음. 즉 개체 그 자신이다. 
}

[docs.oracle.com/javase/tutorial/java/javaOO/thiskey.html]

[개체]

개체는 어떻게 만드는가? ( new 키워드 이해하기)

오라클 문서에서는 new 키워드는 개체를 생성하고, 생성자를 호출한다라고 기술되어 있다.

[docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html]

 

위의 Person 클래스를 개체로 만들어보면, AppClassLoader(ClassLoader).loadClass(String) 를 수행하고, AppClassLoader.loadClass(String, boolean) 를 수행하였다. 

public static void main(String[] args) {
  // Stack 의 지역변수 할당
  Person p;

  // Heap 에 개체 생성 및 이를 지역변수에 할당 
  p = new Person();
}
	

즉 정리하자면, 

1) Person.class 를 로딩하고 -> 이때 한번 적재된 클래스인 경우, AppClassLoader 는 호출되지 않음.

2) 생성자를 통해 개체를 생성함을 확인할 수 있었다. 

 

  • 선택문
  • 반복문

# 0. 들어가기 앞서

 컴퓨터가 없어진지 벌써 1주일이 되었다. 과제를 하기 위해 안드로이드앱에서 지원해주는 자바 에디터도 활용해보았으나 이것은 도저히 사람이 쓸 것이 아니었다.

 1) 갤럭시탭의 Dex모드를 활용하여 Termux 앱 내에 우분투를 설치하고 누군가 만들어놓은 VSCode(웹서버) 를 설치해보았으나 java 관련된 extension 들은 설치할 수 없었다(이것만 해도 시간이 굉장히 오래 걸렸다;;;)

 2) 다음으로는 repl을 활용했지만 약간 이것은 전지구인들이 나의 미약한 코드를 본다는 생각에 아니라고 판단. 

 3) 결국 집에서 열심히 쉬고(?)있는 라즈베리파이(라즈비언lite... 아 젠장) 에 jdk 를 설치하여 HelloWorld를 찍어보았다. 하필 lite라니... GUI가 없다니......... 일단 뭐... 각종 설정하는 것에 한 6~8시간 정도를 소모한 것 같은데 와 "Hello World" 가 이렇게 반가울줄이야. 

  3-1) 어쨋든 IDE는 컴퓨터가 배송될 때 까지는 없다. 

  3-2) 이번주 과제가 2주 짜리인 것은 정말 감사한 일이다. (그전에 컴퓨터를 구할 수 있겠지?!?) 

 

#1 선택문 > 흐름을 제어(선택)하는 문 

 1.1) if - else if - else 문

: 조건으로는 boolean 값만 올 수 있음.

else if인 경우에는 첫번째 if에서는 이미 false 이고, else if의 조건식에서 true 가 해당하는 경우에만 else if 내부의 블록이 수행됨. 

else는 이도저도 다 false 인경우

 

 1.2)  switch 문 

: switch 의 조건식을 계산하여 해당 case 값과 일치하는 것을 선택함.

  . key가 중복된 경우, 먼저 일치하는 값으로 제어를 인도할 줄 알았으나 실제로는 컴파일 단계에서 중복이라고 출력됨 

  . fallthrough(break문이 없는 경우 계속 case문을 흝고 다님...) 하기 때문에, 의도적인 경우 주석으로 이를 알려주는 것이 매너!

코드 편집기 앱인데 정말 기능이 장난아니네;;;


#2 반복문 

for / while 문에 대한 것(+ continue문)은 주석 및 다른 자료를 참고해보면 좋을 듯하다. 

다만 여기서 짚고 넘어가고 싶은 것은 "break" 문이다. 

일반적으로 break문은 반복문을 탈출시키는데 사용되는데,  그렇다면 위에서의 switch 문은 반복문인가? 그것은 아닐 것이다. 

결과적으로 break문은 

   a. switch 문을 종료시키는 기능

   b. 반복문을 종료시키는 기능이 존재한다. 

 

While, For 문 
break문의 이중성(?)

예전에는 공부를 하면서, 그냥 당연한거다 라고 생각을 했었는데, 오히려 배워갈 수록 당연한건 없구나라는 생각이 늘어만 간다. 

 

  • 산술 연산자
  • 비트 연산자
  • 관계 연산자
  • 논리 연산자
  • instanceof
  • assignment(=) operator
  • 화살표(->) 연산자
  • 3항 연산자
  • 연산자 우선 순위
  • (optional) Java 13. switch 연산자

# 연산자

 연산자라함은, 사전적 의미로는 수행되는 연산을 표시하기 위한 기호라고 한다. 

 자바에서는 산술/비트/관계/논리 연산자가 존재하며 그 내용은 아래와 같다. 

 

1) 산술연산자 : 사칙연산을 하기위해 사용되는 연산자 

 다만 주의할 점으로는, 다른 형인 경우에는 더 큰 사이즈의 변수로 형변환을 수행하여 계산을 수행한다. 

값이 모두 float 형으로 출력된다.

2) 비트연산자 : 비트연산을 하기위해 사용되는 연산자 (&, |, ^, >>, <<, >>>)

비트연산은 피연산자들의 값을 비트단위로 논리연산을 수행한다. 그래서 결국 이 친구는 논리연산자인건가??????

 이때 주의사항으로는 아래와 같다. 

 a. byte일 지언정 bit 연산을 수행시는 int로 변환되서 수행된다. 

 b. bit shift를 수행 시, <<< 이 없는 이유는 부호를 고려할 필요가 없기 때문이다. >>> : 0으로 채울 때, >> : 해당 숫자의 부호비트로 채워짐. 

 

3) 관계연산자 : 같다, 같지않다, 작다, 크다를 확인하기 위해 사용되는 연산자

4) 논리연산자 : 논리적인 용어(AND, OR)를 사용하여 조건을 충족하는지 확인하기 위해 사용되는 연산자 

 

# insatnceof

instanceof는 연산자로써 (인스턴스) instanceof (클래스) 로써 형변환이 가능한지의 여부를 boolean 값으로 알려준다.  

# assignment operator (=)

assgitnment operator는 대입연산자이며 위의 예시로 본다면 Person의 객체를 생성하여 Person Type의 변수 a에 대입(할당)해라 라는 의미가 될 수 있겠다. 

 

#  삼항연산자 및 화살표연산자 

삼항연산자 및 화살표연산자 모두 가독성을 해친다고 생각한다. 대신 얻는 것은 내 손이 조금 편해진다? 는..

물론 익숙하지 않아서 가독성을 해친다고 생각하는 내가 문제지

 

 - 삼항연산자: 구성은 "조건식 ? 참일때 수행 : 거짓일때 수행" 이다. 삼항 연산자 이지만, 엑셀(?)처럼 추가적으로 조건을 더 달아줘도 최종 대입연산자를 통해 할당되는 형만 일치한다면 지장은 없다. 

 - 화살표연산자: 화살표 연산자는 람다식에서 사용되며 기본적인 아이디어는 아래와 같다. 

    :  (매개변수) -> (수행될 코드)

   Swift 에서는 Closure 로 사용되는데 (물론 다른 언어에서의 closure와는 다르다고 들었다) 쉽게 얘기해서 이름이 없는 함수로 이해하였다. 다만 여기서의 문제는 단순 최소값을 돌려주는 람다식을 이클립스에서 만들어봤는데 "The target type of this expression must be a functional interface" 라는 에러를 돌려줘서 다음에 다시 보충해서 코드도 작성할 예정이다;;; 

 

 

# 연산자 우선순위 

: 사칙연산에서 곱셈과 나눗셈이 덧셈과 뺄셈에 우선순위를 지니듯, 연산자에서도 우선순위가 존재한다. 

 규칙으로 분류하면 산술연산자 > 관계 연산자 > 논리연산자 > 대입연산자 이다. 합리적인 선에서 이해가 대부분 될테지만...

누가먼저인가????

 요런건 사실 헷갈릴 수 있는 부분이었다. 물론 IDE에서 비트연산에 괄호를 쓰라하여 우선순위를 합리적인 선에서 이해가 가게끔 도와주지만, 실제로 이런 경우에는 비교 연산자가 먼저 적용됨으로써 int형 데이터와 boolean 데이터를 & 하기 때문에 에러다. 

 

# java 13, switch 연산자 

 크게 변동사항으로는 switch 연산자 + yield문을 통해 값을 돌려줄 수 있다는 내용이다.

		// Before JDK 13
		int httpCode  =  400; 
		String meaning = "";
		
		switch(httpCode) {
		case 200:
			meaning = "Success";
			break;
        
        (etc.....)
			
		}
		
		// JDK 13 
		int httpCode  =  400;
		String meaning = switch(httpCode) {
		case 200:
			yield "Success";
		
        (etc....)
        
		}

티스토리 코드블럭을 자바로 변경하는건 태블릿이라 안되는건지 정말 열받는다..

# 학습해야 하는 목록 

  • 프리미티브 타입 종류와 값의 범위 그리고 기본 값
  • 프리미티브 타입과 레퍼런스 타입
  • 리터럴
  • 변수 선언 및 초기화하는 방법
  • 변수의 스코프와 라이프타임
  • 타입 변환, 캐스팅 그리고 타입 프로모션
  • 1차 및 2차 배열 선언하기
  • 타입 추론, var

1. 프리미티브 타입 종류와 값의 범위 그리고 기본 값 

프리미티브 타입은 값을 지니고 있으며, 범위와 기본값은 아래와 같다. 

종류 예약어 메모리 크기 기본값 범위 
논리형 boolean 1 byte  false true, false
정수형 byte 1 byte 0 -128 ~ 127
short 2 byte 0 -32768 ~ 32767
int 4 byte 0 -2147483648 ~ 2147483647
long 8 byte 0L -9223372036854775808 ~ 9223372036854775807
실수형 double 4 byte 0.0f 1.4E-45 ~ 3.4028235E38
float 8 byte 0.0d 4.9E-324~1.7976931348623157E308
문자형 char 2 byte \u0000 0 ~ 65535

 

항상 이런 내용은 기억이 잘 안나는데, 왜 항상 이런 내용을 스터디 하는지는 늘 궁금한 부분이다.

short 까지 범위는 어떻게 기억이 나지만 그 이상은 사실 늘 찾아본다. -_-;;

 

2. 프리미티브 타입과 레퍼런스 타입 

이렇게 위에서 정의된 프리미티브 타입을 제외한 타입을 모두 레퍼런스 타입이며, 이는 주소값을 지니고 있다.

 

3. 리터럴 

찾아보니 리터럴과 상수를 많이 비교들을 하는데 사실 이것들이 왜 비교가되고 있는지 모르겠다. 상수는 항상 같은 값을 뜻하는 것일테고, 리터럴은 그냥 단지 어느 특정한 값을 의미한다. 상수는 이미 어떤 값에 이름이 부여된 상태가 변하지 않는 것일테고 리터럴은 그냥 값이라고 생각된다. 

 

4. 변수 선언 및 초기화하는 방법 

아래의 예시 외에 더 좋은 표현이 있을까? 

int year;    // 변수 선언
year = 2020; // 변수 초기화 

int month = 11; // 변수의 선언 및 초기화

 

5. 변수의 스코프와 라이프타임 

스코프는 범위를 뜻한다. 라이프타임과 결합되어 결국 변수의 생명주기(?)를 뜻한다 

블록("{~}")범위인지, 전역 범위인지에 따라 접근에 대해 다르다. 

가령 블록 범위 내의 변수는 블록 밖에서는 접근이 안될 것이다. 이게 다른 언어에서는 클로저라든지 연관되어 헷갈리는데 무튼 나도 아직 이해는됬으나 자유자재로 코드를 쓰고 있지 못해서 슬프다 - _ㅠ

 

6. 타입 변환, 캐스팅, 그리고 타입 프로모션 

 - 타입변환, 캐스팅은 같은 목적이며 개념 상 형(Type)의 변환을 의미한다. 실생활의 비약이 심각한 예시를 들어보면 500cc 맥주를 주문했다고하자.. 이떄 친구가 너 맥주 몇미리 시켰어? 라고 물었을 때 나는 500cc 라고 대답하는 경우 친구가 못알아듣는 것이다(...) 즉 우리는 타입 변환을 하여 "응 친구야 500ml를 시켰어" 라고 얘기를 해줘야 하는 것이다. 

 

- 타입프로모션 

 프로그램 실행 도중에 자동적으로 형변환(타입변환)이 일어나는데 작은 메모리 크기의 데이터 타입을 큰 메모리 크기의 데이터 타입으로 변환하는 행위를 말한다. 

// byte -> short : ok 
byte a = 127;
short b = a; 

// short -> byte : nok 
short c = 32767;
byte d = c;

7. 1차 및 2차 배열 선언하기 

배열의 본질은, 같은 타입의 여러개를 하나의 배열 변수에 저장하는 것이다. 

이때 1차 및 2차로 선언할 수 있고 첫번째 시작(인덱스)은 항상 0부터이다. 

 

 

8. 타입 추론, var  

타입추론은 컴파일러가 그 타입을 유추하는 것을 의미한다.

// Before var 
String myName = "MyName"; // String 

// Using var 
var myName = "MyName";
System.out.println(myName instanceof String); // 타입 추론에 의해 myName은 String이며 이를 String인지 확인하면 true가 출력됨.

# 학습해야 하는 목록 

  • JVM이란 무엇인가
  • 컴파일 하는 방법
  • 실행하는 방법
  • 바이트코드란 무엇인가
  • JIT 컴파일러란 무엇이며 어떻게 동작하는지
  • JVM 구성 요소
  • JDK와 JRE의 차이
  • javac 옵션 조사

---------------------------------------------------------------------------------

1. JVM이란 무엇인가 

- JVM 은 Java Virtual Machined의 약자이다. 자바의 모토가 Write Once, Run Anywhere 라는데, 이를 지원하기 위해 존재한다고 생각한다. 보통 일반적인 컴퓨터들은, 하드웨어 위에 운영체제가 있고 이 위에 프로그램들이 존재하는데, 자바 가상 머신이 추가됨으로써, 물론 OS 별로 VM 은 다르겠지만 프로그램을 구성하는 코드들을 동일하게 취할 수 있다는 점이다. 

 

2. 컴파일 하는 방법 (w/ javac 옵션)

 - 컴파일은 프로그래머가 작성한 코드를 바이트코드로 변환하는 것을 의미하는데 이는 JVM이 알아먹을 수 있는 내용으로 변환하는 것이다. 

 

[출처: docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html]

javac [ options ] [ sourcefiles ] [ classes ] [ @argfiles ]

 - javac는 컴파일을 하라는 명령어 이고, 

 - options에는 컴파일 옵션을 주는 것이다. 필요한게 있으면 출처의 docs를 참고하여 찾아보자.

   나같은 경우는 예전에 Proguard 노가다를 하다  -nowarn, -verbose 를 썼던 기억이 있다. 물론 -nowarn은 에러외에는 보고 싶지않아서.. -verbose는 에러가 대체 어떻게 났는지 상세하게 알고 싶어서 사용했다; 

 

   무튼 옵션들을 찬찬히 보니, 쓸만한 건(?) 아래와 같다 

     1) -d 의 xxx.class파일들의 생성된 결과물의 디렉터리를 지정하는 것. 

     2) -source <버전> 을 통해 버전을 명시해주는 것은 관리차원에서 좋을 것 같다... 다만 1.5로 설정후 java로 실행해도 별 문제는 없었다. 내 컴퓨터에 java 버전이 많았으면 뭔가 더 재밌는 테스트를 해볼 수 있겠지만 일단 여기까지(...) 

       

 - classes에는 컴파일할 대상 파일 

 

3. 실행하는 방법 

[출처: docs.oracle.com/javase/7/docs/technotes/tools/windows/java.html

java [ options ] class [ arguments ]

java [ options ] -jar xxxx.jar [ arguments ]

 - java는 실행하라는 명령어이고, 

 - options에는 실행옵션을 주는건데, 나는 주로 -jar를 통해 OOOO.jar 파일을 실행해본적만 있다. 

 

4. 바이트코드란 무엇인가? 

다른 사람들 과제를 보니, 물론 다 본건 아니지만 가상머신이 이해할 수 있는 0과 1로 구성된 이진코드라고 하는데, 이진코드...라 이진코드면 바이너리 코드가 아닌가라는 생각이 들었다. 음 일단 뭐 찾다보니 맞는 얘기인 것 같은데 이진표현법이라면서 파일을 읽어보면 "^@^B^@^@^@^C^@^H^@^D^@^A^@^M^@^@^@^B^@^N" 이런 외계어들이 가득하다 

jd-gui 통해서는 잘도 .java 로 돌아가면서 .class파일은 이게 이진표현법이라면서 뭔가 이진표현법이 맞나? 싶다 -_-; 

 

결론적으로 바이트코드란 것은 프로그래머가 작성한 코드를 "자바 컴파일러(javac)"가 가상머신(자바라면 JVM , 안드로이드(구)라면 DVM)이 기계어로 변환할 때 필요한 재료라고 생각한다. 

 

5. JIT 컴파일러란 무엇이며 어떻게 동작하는가?

[정의] 

이어서 작성하자면 결국 JIT 컴파일러란, 바이트코드를 여러 기계에서 돌아갈 수 있게끔 해당 기계어로 변환해주는 것이다. 

어떻게 동작하는지는 다음의 구성요소에서 자세히 작성하겠다. 

 

6. JVM의 구성요소는 ? 

앞서 5번에서 작성한것을 기반으로 간략히 말하자면,  JVM의 구성요소는 Class Loader, Execution Engine, Runtime Data Area 로 구성되어 있다. 

 

1) Class Loader는 클래스를 로딩하는 역할을 담당한다.

 대상: jre/lib/rt.jar , jre/lib/ext/*.jar , 기타 등등... 

 순서: 

  - 클래스 Loading : .class 파일의 적재 

  - 클래스 Linking : .class가 잘 돌아갈지 "검증"하고 / 필요한 것들을 "준비"

  - Initiailation :  클래스 변수들의 "초기화:

 

2) Execution Engine은 실행하는 역할을 담당하며, Interpreter, Compiler(JIT), GC(Garbage Collector)으로 구성되어 있다. 

 - Interpreter : 바이트코드를 한줄한줄 기계어로 변환해준다. 이때, 중복되는 코드가 있더라도 한줄한줄 실행하기에 그대로 수행한다. 

 - Compiler(JIT) : Interpreter의 단점(비효율성)을 해결하기 위해 도입되었으며 반복적인 코드들을 미리 기계어로 준비해두어 필요할 때 사용한다. 

 - GC(Garbage Collector) : Heap Area에서 안쓰는 것(? : 추후 자세히 정리할 예정...)들에 대해 메모리를 자유롭게 해준다

 

3) Runtime Data Area 는 아래와 같이 구성된다

 - Method Area : 클래스, 변수, 상수등이 모여있는 공간

 - Heap Area : 클래스의 인스턴스 들이 모여있는 공간

 - Stack Area : 지역변수, 매개변수 들이 모여있는 공간

 - PC Register : 프로그램카운터, 즉 현재 수행중인 명령의 주소값이 저장되는 공간

 - Native Method Stack : 다른 언어의 메소드 호출을 위해 할당된다는데, 사실 잘 와닿지는 않는다. 

 

 

7. JDK와 JRE이 차이 

JDK는 Java Development Kit의 약자로, 자바 프로그램들을 구동 및 개발할 수 있게 해주는 환경을 의미하고, 

JRE는 Java Runtime Environment의 약자로, 자바 프로그램들을 구동할 수 있게 해주는 환경을 의미한다. 

 

수정 시 참고할 자료 

homoefficio.github.io/2018/10/13/Java-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A1%9C%EB%8D%94-%ED%9B%91%EC%96%B4%EB%B3%B4%EA%B8%B0/

 

Java 클래스로더 훑어보기

Java ClassLoader 훑어보기아주 예전에 SCJP 시험볼 때나 살펴본 이후로 자바의 클래스로더를 직접 다뤄야 할 일은 솔직히 없었다. 그래서 거의 잊고 살아왔는데 요즘 Quartz를 다루면서 Quartz에 없는 기

homoefficio.github.io

 

d2.naver.com/helloworld/1230

javapapers.com/core-java/java-jvm-run-time-data-areas/#Program_Counter_PC_Register

 

Java JVM Run-time Data Areas - Javapapers

Understanding Java Virtual Machine (JVM) run-time data areas are critical to better Java programming. One of the most dreaded errors in Java is OutOfMemoryError and it is related to Java Virtual Machine (JVM) memory areas. We should have better understandi

javapapers.com

docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ClassLoader.html

 

ClassLoader (Java SE 11 & JDK 11 )

Converts a ByteBuffer into an instance of class Class, with the given ProtectionDomain. If the given ProtectionDomain is null, then a default protection domain will be assigned to the class as specified in the documentation for defineClass(String, byte[],

docs.oracle.com

 

+ Recent posts