본문 바로가기

JAVA/JAVA 기초

예외 관련

반응형

예외


1. 검사예외(Exception)

주로 프로그램 작성시 에 예상할 수 있는 비정상 상태를 통지하기 위해서 사용한다.

파일에서 데이터 입출력하는 처리에서 파일을 읽고 쓸수 없는 경우 IOException 예외발생

검사예외를 사용하면 예상되는 비정상 상태에 대응하는 처리가 있는지 컴파일 시 체크한다.

포착(catch)해서 처리하거나 상위 호출원에 대해 예외를 발생시키는(throw)것이 필수이며 하지 않을 경우 컴파일 오류가 발생한다.


2.실행시예외(RuntimeException)

주로 프로그램 작성시에 예상할수 없는 오류를 통지하기 위해서 사용한다.

포착하지 않아도 컴파일 오류가 발생하지 않는다.

포작하지 않을 경우 무조건 호출원에서 발생하게 된다.

Integer클래스의 parsInt 메서드를 호출할때 정수로 변환할수 없는 문자열을 인수로 지정하면 NumberFormatExeption이 발생한다.

그러나 Integer클래스의 parseInt 메서드는 String객체를 int 타입으로 변한하기 위한 API이므로 정수로 변환할수 없는 문자열을 인수로 지정하는 것은 예상 외의 이용방법이다.

따라서 일반적인으로 호출원에서 예외를 포착할 필요가 없다.

이러한 예뢰를 모두 검사 예외와 마찬가지로 반드시 포착해야 한다면 프로그램이 복잡해져서 파악하기 어렵게 된다.


3.오류(Error)

예외와 달리 시스템의 동작을 계속할 수 없는 치명적 오류를 나타낸다.



Exception클래스

검사 예외를 나타내는 클래스

-파일이나 네트워크 등의 입출력 중에 발생한 에러를 나타내는 IOException 클래스

-데이터베이스 액세스 중 발생한 에러를 나타내는 SQLException 클래스


public List<String> readFile() throws IOException {


}


throws 절을 메서드의 시그니처에 기술하면 그 처리에서 예외가 발생한다는 것을 알 수 있다.

시그니처(선언의 내용)에 예외가 기술된 메서드는 호출하는 쪽에서 그 예외에 대해 무언가의 처리를 하도록 자바의 언어 사양상 요구되고 있다.

예외를 처리하는 코드를 기술하고 있지 않다면 컴파일 오류가 발생한다.

처러해야하는 예외를 언어 사양으로 체크(검증)하도록 되어 있으므로써 라이브러리를 이용하거나 여러 사람들이 공동 개발하는 경우에도 처리해야 할 예외를 빠뜨리지 않게 된다.

try~catch 블록이 필요하게 된다.


    public static Path createTempFile(String prefix,

                                      String suffix,

                                      FileAttribute<?>... attrs)

        throws IOException

    {

        return TempFileHelper.createTempFile(null, prefix, suffix, attrs);

    }



public static void main(String[] args) throws IOException{

        Path path = Files.createTempFile("test-file", ".txt");

        Files.write(path, "test content...".getBytes());

}

호출하는쪽에서 예외처리를 해야한다.

호출하는 메서드에서 throws IOException 포함하거나

public static void main(String[] args){

try {

Path path = Files.createTempFile("test-file", ".txt");

Files.write(path, "test content...".getBytes());

}catch (IOException e) {

}finally{

}


try~catch 블록으로 예외처리 방법




RuntimeException클래스

실행 시 예리를 나타내는 클래스

이 클래스를 상속한 예외는 프로그램안에서 반드시 포착할 필요도 없고, 메서드의 시그니처에 thorws 절을 기술할 필요도 없다.

catch블록, throww절도 기숳하지 않은 경우 발행한 실행 시 예외는 호출원으로 자동으로 전파된다.

실행시 예외를 어디서도 포착하지 않는 경우, 그 스레드가 종료해 버린다.

스레드를 시작한 처리 차체에는 예외가 전파되지 않는다.

실세로 스레드를 시작하고 있는 것은 java VM이다.

java VM에 예외가 도달한 시점에 그 스레드를 종료해 버린다. 스레드가 java VM의 메인 스레드인 경우 애플리케이션 자체가 종료한다.


Error클래스

일반적인 애플리케이션에서는 포착해서는 안되는 중대한 문제를 나타내는 클래스

자바의 예외 메커니즘의 관점으로 볼때 Error는 RuntimeException과 비슷하여 catch블록, throws절도 기술할 필요가 없다.

Error가 발생하는 상황은 대부분의 경우 애플리케이션이 비정상의 상태에 빠져 있어 신속하게 프로그램을 종료시켜야하는 상황이다.

OutOfMemoryError 클래스는 자바가 사용하는 메모리가 부족하거나 할 때 발생한다.

이 오류가 발생한 경우는 로그 출력 조차도 할 수 없는 상태로 최악의 사태라고 할수 있기 때문에 신속하게 종료해야 한다.



              Throwable


Exception                  Error


RuntimeException




예외처리

try~catch~finally


try {

} catch(IOException e) {

//예외를 포착한 경우의 처리

} finally {

//리소스를 해체하는 처리

}



BufferedReader bufferedReader = null;

        try{

        bufferedReader = new BufferedReader(

        new InputStreamReader(Files.newInputStream(rn_demo)));

        bufferedReader.readLine();

        } catch (IOException e) {

        System.err.println(e);

        }finally {

        bufferedReader.close();

        }


try~with~resource


Path rn_demo = Paths.get("C:\\", "sample.xml");

        String line;

        try (BufferedReader bufferedReader = new BufferedReader(

    new InputStreamReader(Files.newInputStream(rn_demo)))) {

        while ((line = bufferedReader.readLine()) != null) {

        System.out.println(line);

        }

        } catch (IOException e) {

        System.err.println(e);

        }


자바7부터 리소스를 취급하는클래스는 java.lang.AutoCloseable 인터페이스 또는 java.io.Closeable 인터페이스를 구현하도록 되었다.


public interface Closeable extends AutoCloseable {

public void close() throws IOException;

}


try(...)블록의 시작시에 AutoCloseable 인터페이스의 구현 클래스를 선언해두면 해당 try~catch 블록의 종료시의 처리에서 실시할 close 메서드를 자동으로 호출하게 된다.



다중캐치

try블록안에서 여러 예외가 발생하는 경우도 있다.

각각의 처리에 catch 블록으로 작성하는 것도 가능


catch(ClassNotFoundException ex) {


}catch(InstantiationException ex) {


}catch(IllegalAccessException ex) {



복수의 예외에서 동일 처리하고 싶은 경우

catch(ClassNotFoundException |

InstantiationException |

IllegalAccessException  ex) {


}


귀찮다고 해서 Exception으로 처리하는건 좋지않다.




예외처리방법


1.오류코드의 반환하지 않기

자바에서는 예외 메커니즘이 언어의 사양으로 제공되기 때문에 오류가 발생하면 예외를 발생시켜야한다.

정상적으로 처리가 종료하면 그 객체를 반환값으로 반환하면된다.

호출하는 쪽에서는 메서드가 종료해서 반환값을 얻었으니 처리 자체는 성공했다고 생각하기 때문에 호출한 쪽에서 불필요한 if문을 작성할 필요가 없게 된다.


    public String getValueFromFile(File file) throws IOException {

    Properties props = new Properties();

    props.load(Files.newInputStream(Paths.get(file.getPath())));

   

    //파일로딩이 실패한 경우 내려오지 않는다.

    //props가 파일을 로딩했는지를 if문으로 판정할 필요가 없다.

 

    String value = props.getProperty("spring.h2.console.enabled");

    return value;

    }


단, 모두 예외를 throw 하면 되는건 아니다.

isXxx 메서드라는 원래부터 판정처리를 의도한 메서드(boolean을 반환)는 판정 결과가 false가 되는 경우에도 판정 자체가 실시되었다면 처리가 성공했다고 인정하고, 예외는 throw 하지 않도록 해야한다.


2.예외를 제거하지 않기


1).로그출력

최소 로그를 출력하면 언젠가 로그를 확인해서 장애의 원인을 알 수 있을지도 모른다.

예외의 스택 트레이스도 로그에 출력한다.

예외가 발생하는 곳과 관련된 클래스와 메서드의 호출 과정이 정보로 담겨 있다.


String strValue ="abc";

try {

int intValue = Integer.valueOf(strValue);

System.out.println("intValue is" + intValue);

}catch(NumberFormatExceptioin ex) {

log.warn("숫자가 아닙니다.");

log.warn("숫자가 아닙니다." + ex);

log.warn("숫자가 아닙니다.", ex);

}


오류 메시지의 차이점이 있다.

예외의 스택 트레이스를 로그에 출력해두자.


2).처리를 할지판단

처리를 계속할지를 명확하게 하는것.

예외가 발생한 행에서 catch 블록까지 처리가 넘어간 결과, 객체의 상태가 변경되지 않는 경우가 있다.

catch블록에 처리가 넘어간 시점에서 객체의 초기화가 완료되지 않았거나, 취득해야 할 값을 얻지 못하거나 하는 경우, 처리를 계속하더라도 결국 NullPointException이 발생하는 등 의미가 없는 처리가 될 수 있다.


String strValue = null;

try {

strValue = props.get("key");

}catch(IOException ex) {

log.warn("프로퍼티 키를 찾지 못했습니다.", ex);

}


if(strValue.length() < 5) {

log.error("5문자 이상 필요");

return;

}



대부분의 경우 예외가 발생하면 후속 처리를 중단하고 즉시 복구하거나 상위 메서드에 예외를 throw하게 된다.

처리를 계속하는 경우는 디폴트값을 줘서 그 이후의 처리를 계속해도 된다.


String strValue = null;

try {

strValue = props.get("key");

}catch(IOException ex) {

log.warn("프로퍼티 키를 찾지 못했습니다.", ex);

strValue = "default";

}


if(strValue.length() < 5) {

log.error("5 문자 이상 필요");

return;

}



3.throws Exception 감염

복수의 예외가 발생하더라도 예외의 선언이나 포착이 귀찮기 때문에 메서드의 시그니처에 throws Exception이라고 쓰는 경우가 있는데, 사용하지 않도록 한다.


1)호출하는 곳에서 Exception을 포착해야한다.

검사 예외는 호출하는 쪽에서 이것을 포착허거나, 상위로 다시 throw 해야한다.


public void caller() {

try{

callee();

}catch(Exception ex) {

//포착해야한다.

}

}


private void callee() throws Exception {

}


callee 메서드에서 실제로 예외가 발생하지 않는다 하더라도 caller 메서드에서는 Exception 클래스의 catch 블록을 작성할 필요가 있다.

이 코드는 1계층의 호출밖에 없는 단순하지만, 여러 계층에 걸쳐 있다면 상위 호출원은 왜 예외를 포착해야 하는지 모르겠다라는 상태가 될 것이다.


2) 도중에 IOException 등 구체적인 예외가 발생한다 해도 Exception에 휩쓸려 버린다.

Exception 클래스는 모든 예외의 기저 클래스이므로 IOExcepton 등 구상 클래스의 예외가 발생해도 throw Exception으로 선언이 가능하다.

Exception으로 선언해 버리면 호출하는 쪽이 Exception이 실제로 구상 클래스의 예외였다고 해도 그 종류를 이해해서 코드를 작성 할수 없게 된다.


public void caller() {

try{

callee();

}catch(Exception ex) {

//발생한 예외의 종류로 catch 블록을 나눌수 없다.

}

}


private void callee() throws Exception {

//검사 예외가 발생할 가능성이 있다.

//ClassNotFoundException

//InstantiationException

//IllegalAccessException

// 메서드 호출에서 다음의 검사 예외가 발생할 가능성이 있다.

Class someClass = Class.forName(className);

someClass obj = someClass.newInstance();

}


3) 도중에 RuntimeException이 발생해도 Exception으로 휩쓸려 버린다.

RutimeException이 실행시 예외이므로 이쪽이 보다 문제가 될 가능성이 있다.

-실행시 예외로 처래해도 상관없는 경우라도 검사 예외가 동일하게 포착해야한다.

-검사예외라고 생각해서 처리하고 있으면 실행시 예외가 발생한 경우 잘못된 처리를 실시할지도 모른다.

-만약 호출원에서 포착해서 상위로 전파시키지 않으면 실제로 처리가 필요한 실행시 예외를 놓칠수도 있다.


public void caller() {

try {

callee();

}catch(Exception ex) {

//여기서 포착한 예외를 검사예외라고 생각해서 처리하다보면,

//callee 메서드가 NullPointerException이 발생한 경우의

//처리가 적절하지 않을지도 모른다.

}

}



private void callee() throws Exception {

String str = null;

System.out.println("str.length" + str.length());  // NullPointerException이 발생

}


RuntimeException이 발생하는 처리에 대해 Exception을 포착하는 처리를 작성하면 본래 본착해서는 안되는 예외도 의도하지 않게 포착해버리는 일이 있다.

RuntimeException은 프로그램의 버그를 나타내는 경우가 있기 때문에 이것을 포착할 경우 버그의 발견이 늦어질수 있다.

그만큼 RuntimeException을 의도하지 않게 포착해 버리는 것은 위험하며 이러한 사태를 일으킬 가능성이 있는 throws Exception은 피해야 한다.


'개발 단계의 단위 시험이나 통합 시험에서 프로그램의 버그를 없앤다'라는 전제에서 버그가 발생한 경우 예외가 main 메서드를 지나쳐 애플리케이션을 비정상 종료시키는 문제에 대해 인식하고 즉시 대응할 수 있으므로 좋다고 생각할 수 있다.

물론 실제 환경에서 버그가 발생하여 애플리케이션이 비정상적으로 종료해 버리는 것은 문제이기 때문에 애플리케이션이 요구하는 품질 요구사항에 따라 어떤 문제가 발생해도 처리를 계속시키는 구조를 검토할 필요가 잇다


4.어느 계층에서 예외를 포착해서 처리해야 하는가

예외를 취하는 쪽에서  포착할지, 상위로 예외를 발생시킬지를 판단할 필요가 있다.

예외가 발생하는 원인이나 장소는 각가 다르다고 생각하는데 만드는 사림이나 기분에 따라 제각각으로 처리하면 통제가 안된다.

-예외가 발생하는(가능성이 있는)장소

-처리의 흐름을 판단하는 장소


말단의 처리가 되기 때문에 이 경우에는 일일이 개별적으로 처리의 중지 및 회복의 판단을 할 경우 전체적인 흐름이 보이지 않게 된다.

또한 개별로 판단하려고 하면 대상이 상당히 많아 작업량이 엄청나게 많아진다.

양이 많다는 것은 그만큼 틀리기 쉽다는 의미도 있고 어떤 하나에 변경을 가했을 때 동일한 변경을 여러번 해야하는 어려움도 있다.

그러므로 일단 말단의 처리에서는 '예외를 발생시키기만'하는 것이 좋다.


말단처리에서 예외를 포착해야 하는 곳이며, 처리를 계속할지 아니면 중지할지를 이부분에서 판단해야 한다.

규모가 큰 프로그램이 되면 어느 정도는 처리가 큰 덩어리로 여러 계층으로 구성될 것이다. 그런 경우는 가능한 상위의 호출 계층에서 예외를 포착하도록 하는 쪽이 취급하기 쉽다.

예외를 포착하는 계층은 프로그램을 만들려고 시작하기 전인 설계단계에서 인식해두면 좋다.

그리고 try~finally 블록을 실시하도록 한다.


5.독자적인 예외작성

일반적으로 특별한 이유가 없으면 자바 표준 API에 있는 예외 클래스에서 적절한 클래스를 선택하여 사용하는 것이 좋다.

하지만 예외를 개별적으로 처리하는 것이 아니라 통일해서 처리하고 싶은 경우가 있다. 그때 이용하는 것이 독자적인 예외이다.

1) 애플리케이션 예외(ApplicationException) 클래스

애플리케이션의 비즈니스 로직에서 오류가 발생했음을 나타내는 예외를 만든다.

웹 애플리케이션의 경우 해당 처리를 다시 시작할 수 없는 오류가 여기에 해당된다.

예, 화면에서 입력한 데이터가 시스템이 기대하는 포맷에 맞지 않는 경우 ApplicationIllegalFormatExcetion 등과 같이 구체적인 구상 클래스를 만든다.


2) 시스템 예외 SystemException

데이터베이스가 정지, 네트워크가 연결 안됨 등 시스템으로서 정상적인 동작을 유지할 수 없는 경우에 시스템 전체에 영향을 주는 장애가 발생했음을 나타내는 예외를 만든다.

1- 업무에 특화한 처리인 경우(적어도 광범위하게 재사용할 것이 아닌 것)

2- 프레임워크나 시스템에서 공통적인 예외 처리를 하는 경우

두가지 조건을 만족할 경우는 독자적인 예외를 만든다.

독자적인 예외의 장점

- 자바의 표준 API에 있는 예외 클래스와 구별함으로써 예외를 포착하는 쪽은 많은 예외를 인식하지 않아도 된다.

- 업무로직으로서 공통처리를 만들 때 영향 범위를 국소화할 수 있다.


반대로 예외처리를 실시하는 로직이 업무에 의존하지 않는 경우는 표준API에 있는 예외를 사용한다.


독자적인 예외 작성

검사예외 - Exception클래스

실행시예외 - RuntimeException 클래스


public class ApplicationException extends Exception {

public ApplicationException(String message) {

super(message);

}

public ApplicationException(String message, Throwable cause) {

super(message, cause);

}

public ApplicationException(Throwable cause) {

super(cause);

}

}


Exception 클래스에는 그외에도 인수가 없는 생성자 등이 있는데, 독자 예외 클래스에서는 할 수 있는 한 인수를 강제하는 형태로 해야한다.



public class ApplicationException extends Exception {

private String id;

private Object[] params;

public ApplicationException(String id, Obejct... params) {

super();

this.id = id;

this.params = Arrays.copyOf(params, params.length);

}

public ApplicationException(String id, Throwable cause, Obejct... params) {

super(cause);

this.id = id;

this.params =  Arrays.copyOf(params, params.length);

}

public String getId() {

return id;

}

public Object[] getParmas() {

return Arrays.copyOf(params, params.length);

}

}


메세지 대신 오류ID와 매개변수를 지정하는 것도 좋다.

실제 메세지 등을 오류ID에 해당하는 정보로 프로그램의 밖으로 내보낼 수 있게 된다.

팀으로 시스템을 개발하다 보면 메시지 작성 방식이 제각각이 되거나 메시지를 변경하려고 할 때 어디를 수정하면 좋을지 모르는 경우가 있다.

또한 다국어에 대응할 필요가 나올수도 있다.

그런 경우에는 프로그램에 메시지를 넣어두면 유지보수가 힘들어진다.

그래서 메시지 리소스를 속성 파일 등에 따로 두어 로그 출력의 공통 클래스나 예외 핸들러와 같은 구조를 사용하여 메시지로 변환하도록 하면 유지보수성의 좋은 매커니즘이 될수 있다.

로그 메시지에 넣을 매개변수는 가변 길이 인자로 사용할 수 있도록 해둔다.

단 이런 구조는 시스템 개발의 최초로 도입하는 것이 중요하다.


6.예외의 트렌드

1) 검사예외보다도 실행시 예외를 사용한다.

프레임워크에서 제공하는 독자적인 예외는 대체로 실행시 예외로 되어 있다.

프로그램의 시작시에 공통적인 부분을 정리하여 예외로 처리함으로써 예외 처리를 간단하게 하는 목적과 예외을 일일이 개별 로직으로 처리하지 않아도 되기 때문에 로직의 코드도 간단하게 된다는 장점이 있다.

예, 자바8에서는 Stream API를 처리하기 위해 UncheckedIOException을 추가했다.

프레임워크의 독자적인 예외로는 SQLRuntimeException 등의 예외 래퍼가 만들어 질 수 있다.


2) 람다식 안에서 발생한 예외의 취급

자바8에서 등장한 람다식에서는 람다 안에서 기술한 처리에 예외가 발생할 가능성도 있다.

검사 예외인 경우는 포착하지 않으면 컴파일 오류가 발생한다.

RuntimeException인 경우 람다식을 호추한 처리가 예외가 throw 된다.


try(BufferedWriter writer = Files.newBufferedWriter(Paths.get(W_FILENAME))) {

//writer.write()가 IOException을 throw하므로 포착하도록 한다.

lines.forEach(s -> writer.write(s + "\n"));

} catch (IOException ioex) {

System.out.println("IOException in writer-try.");

ioex.printStackTrace(System.out);

throw new UncheckedIOException(ioex);

}


람다 안에서 발생한 실생 시 예외를 throw하면 람다의 바깥 쪽에 에외를 전달할 수 있다.

하지만 예를들어 (parallelStream 메서드를 사용하는 등의) 병렬 실행을 하는 경우는 모든 예외를 수취할 수 있는 것은 아니다.

그러한 경우를 고려하여 람다 안에서 모든 예외를 처리하는 것이 좋다.


try(BufferedWriter writer = Files.NewBufferedWriter(Paths.get(W_FILENAME))) {

lines.forEach( s -> {

try{

writer.write(s + "\n");

} catch (IOException ex) {

System.out.println("IOException in lambda.");

ex.printStackTrace(System.out);

throw new UncheckedIOException(ex);

}

});

} catch(IOException | RuntimeException ex) {

System.out.println("Exception in writer-try");

ex.printStackTrace(System.out);

throw ex;

}


3) Optional 클래스의 도입에 장점

자바 8부터는 Optional 클래스가 도입되었다.

Optional 클래스가 도입된 것은 자바가 NullPointerException을 발생하기 쉬운 언어인 점을 개선하기 위해서다.

자바 프로그래밍을 하는데 있어서 API를 호출했을 때 반환값이 존재하지 않은 경우 null을 반환하는 API가 많이 있다.

java.util.Map 클래스의 get 메서드는 지정된 키에 해당하는 값이 존재하지 않을 경우 null을 반환한다.

null 체크를 하지 않아도 컴파일 시는 오류가 발생하지 않는다.

따라서 null체크를 하지 않으면 NullPointerException이 발생한다.


Map<String, String> map = new HashMap<>();


String value = map.get("key1");


if(value != null) {

System.out.println(value.length());

}else {

System.out.println("null value");

}


Optional

메서드

Optional<T> optional = Optional.of(T value) / 값을 갖는 Optioanl 객체를 반환한다.

Optional<T> optional = Optional.empty() / 값을 갖지 않는 Optional 객체를 반환한다.

T value = optional.get() /  값을 반환한다. 값을 갖지 않은 Optioanl인 경우에는 예외가 발생하기 때문에 isPresent 메서드로 확인한 후에 사용한다.

optional.isPresent() / 값을 갖는 경우 true, 값을 갖지 않는 경우 false를 반환

optional.isPresent(value -> {...}) / 값을 갖는 경우만 람다식의 처리를 실시한다.



public calss OptionalStack<E> {

private List<E> taskList;

public OptionalStack() {

this.taskList = new ArrayList<>();

}

public boolean push(E task) {

return this.taskList.add(task);

}

public Optional<E> pop() {

if(this.taskList.isEmpty()) {

return Optional.empty();

}

return Optional.of(this.taskList.remove(this.taskeList.size()-1));

}

}



OptionalStack<String> optStack = new OptionalStack<>();

Optional<String> optional = optStack.pop();  //push()하지 않았으므로 값을 갖고 있지 않는 optional를 반환


String optElement = optioanl.orElse("empty"); //optional의 값이 존재하지 않은 경우는 'empty'를 반환


System.out.println(optElement); //empty


optStack.push("Scala");

optStack.push("Groovy");

optStack.push("Java");


optional = optStack.pop();


if(optional.isPresent()){ // optional의 값이 존재하는 경우에만 

System.out.println(optional.get()); //Java

}


optional = optStack.pop();


optional.ifPresent(System.out::println) //Groovy


Optional 객체를 취득한 후에 개발자는 Optional 객체가 값을 갖고 있는지, 안갖고 있는지를 명확하게 의식해서 코드를 작성해야 한다.

isPresent 메서드로 값을 갖는지의 여부를 확인하는 것은 의미상 null 체크를 하고 있는 것과 다르지 않다.

그러나 Optional 클래스를 이용함으로써 API 이용자 스스로가 의식해서 null체크에 신경쓰게 하는 것이 아니라 API가 Optional 객체를 반환함으로써(좋은 의미에서) 강제적으로 갖는 경우와 갖지 않는 경우의 코드를 작성하도록 한다.

게다가 orElse 메서드나 람다식에 처리를 전달할 수 있는 편리한 메서드가 준비되어 있기 때문에 프로그램이 간단해진다.

Optional 클래스는 객체를 취급하는데 기본형인 int, long, double을 취급하고 싶은 경우에는 각각 OptioanlInt, OptionalLong, OptionalDouble 클래스를 이용한다.

출처: 자바 마스터북

반응형

'JAVA > JAVA 기초' 카테고리의 다른 글

값 전달 방법(callByValue callByReference)과 불변객체  (0) 2018.10.25
문자열관련  (0) 2018.10.22
자바 컬렉션관련  (0) 2018.10.13
자바 배열관련  (0) 2018.10.13
자바 타입관련  (0) 2018.10.13