-
자바 스트림, reduce, 예외 처리, logging언어/Java 2023. 7. 14. 18:17728x90반응형SMALL
스트림
- 자료의 대상과 관계없이 동일한 연산을 수행합니다.
- 배열과 컬렉션을 대상으로 연산을 수행합니다.
- 일관성 있는 연산으로 자료의 처리를 쉽고 간단하게 합니다.
- 한 번 생성하고 사용한 스트림은 재사용할 수 없습니다.
- 스트림 연산은 기존 자료를 변경하지 않습니다.
- 중간 연산과 최종 연산으로 구분됩니다.
- 중간 연산은 여러 개의 연산이 적용될 수 있지만, 최종 연산은 마지막에 한 번만 적용됩니다.
- 최종 연산이 호출되어야 중간 연산에 대한 수행이 이루어지고 결과가 만들어집니다.
- 중간 연산에 대한 결과를 연산 중에는 알 수 없습니다.(이를 지연 연산이라고 합니다.)
스트림 생성
int[] arr = {1,2,3,4,5}; int sum = Arrays.stream(arr).sum(); long count = Arryas.stream(arr).count();
정수 배열에 스트림 생성하여 연산을 수행하는 예시입니다.
중간 연산 & 최종 연산
- 중간 연산의 예 -
filter()
,map()
,sorted()
등이 있습니다. - 최종 연산의 예 -
forEach()
,count()
,sum()
등이 있습니다. - 최종 연산이 호출될 때 중간 연산이 수행되고 결과가 생성됩니다.
- 중간 연산과 최종 연산에 대한 구현은 람다식을 활용합니다.
- 문자열 리스트에서 길이가 5이상인 문자열을 출력하는 예시입니다.
list.stream().filter(s->s.length() >= 5).forEach(s->System.out.println(s));
- 클래스 배열에서 원하는 멤버 변수만 가져오는 예시입니다.
classList.stream().map(instance->instance.getName()).forEach(str->System.out.println(str));
- ArrayList stream 예시
List<String> list = new ArrayList<String>(); list.add("111"); list.add("222"); list.add("333"); Stream<String> stream = list.stream(); stream.forEach(str->System.out.println(str)); stream.mapToInt(str->Integer.parseInt(str)).forEach(str->System.out.println(str)); //에러 list.stream().sorted().forEach(str->System.out.println(str)); System.out.println(list.stream().mapToInt(str->Integer.parseInt(str)).sum());
한 번 연산을 한 스트림의 경우 재사용이 불가능합니다. 새롭게 스트림을 생성해줘야지만 연산이 가능합니다.
중간 연산
distinct
: 중복을 제거해줍니다.filter
: 조건에 맞는 요소만을 필터링해줍니다.limit
: 배열의 길이를 제한합니다.skip
: limit과 반대로 건너 뛸 요소들을 정의합니다. (skip(2)가 주어진다면 1,2 번째는 건너 뛰고 3번째 요소부터 반환됩니다.)peek
: 요소마다 작업할 수 있도록 해줍니다. (넘겨 받은 스트림을 변경시키지는 않고 디버깅 용도로 현재 스트림까지의 연산이 어떻게 진행되었는지 확인 용도 등에 사용될 것 같습니다.) 중간 연산이므로 최종 연산이 뒤에 있어야지만 실행됩니다.sorted
: 배열을 정렬시켜줍니다.map
,mapToDouble
,mapToInt
,mapToLong
: 요소마다 변환시킬 수 있도록 해줍니다.
최종 연산
forEach
,forEachOrdered
: 요소 마다 작업 할 수 있도록 해줍니다.count
: 요소 개수를 반환해줍니다.max
,min
: 최대 최소값을 반환합니다.findAny
,findFirst
: 원하는 요소를 찾아줍니다.allMatch
,anyMatch
,noneMatch
: 주어진 조건에 만족한 요소가 있는지 확인해줍니다.toArray
: 스트림 요소를 배열로 반환해줍니다.reduce
: 요소를 리듀싱해줍니다.collect
: 컬렉션에 담아 반환합니다.
reduce()
연산- 사용자가 직접 구현한 연산을 적용할 수 있습니다.
- 최종 연산으로 스트림의 요소를 소모하며 연산을 진행합니다.
- 실제 연산은 람다식을 통해 구현할 수 있습니다.
- 람다식이 길 경우
BinaryOperator
를 구현한 클래스를 사용합니다. - 배열의 모든 합을 구하는 연산의 예시입니다.
list.stream().reduce(0,(a,b)->a+b));
BinaryOperator
구현 예시
class CompareString implements BinaryOperator<String> { @Override public String apply(String s1, String s2){ if(s1.getBytes().length >= s2.getBytes().length)return s1; else return s2; } } String[] array = {"1","555","22","444","333"}; System.out.println(Arrays.stream(array).reduce("",(s1,s2)->{ if(s1.getBytes().length >= s2.getBytes().length)return s1; else return s2; }); System.out.println(Arrays.stream(array).reduce(new CompareString()).get());
예외 처리
컴파일 오류
- 프로그램 코드 작성 시 발생하는 문법적 오류
- IDE를 통해 찾아내기 쉬움
실행 오류(runtime error)
- 실행 중인 프로그램이 의도 하지 않은 동작(bug)를 하거나 프로그램이 중지되는 오류입니다.
- 비정상 종료의 원인이 되며, 시스템에 심각한 장애가 발생할 수 있습니다.
예외 처리의 중요성
- 프로그램의 비정상 종료를 피하여 시스템을 안정적으로 실행하도록 해줍니다.
- 오류가 발생한 경우 로그를 남기도록 하고, 이를 통해 오류에 대한 원인 파악이 쉽도록 할 수 있습니다.
오류
- 가상 머신에서 발생, 프로그래머가 처리 할 수 없는 오류입니다.
- 스택 오버플로우 등...
예외
- 프로그램에서 제어 할 수 있는 오류입니다.
- 파일 명이 존재하지 않는 경우, 네트워크나 데이터베이스 연결이 안되는 경우입니다.
예외 클래스
- 모든 예외 클래스의 최상위 클래스는 Exception 클래스입니다.
- 자바에서 다양한 예외들의 처리를 위한 클래스가 제공되고 있습니다.
ArithmeticException
: 정수를 0으로 나눈 경우 발생합니다.NullPointerException
: 초기화 되지 않은 Object를 사용하는 경우 발생합니다.ArrayIndexOutOfBoundsException
: 배열의 크기를 넘어선 위치를 참조하려는 경우 발생합니다.FileNotFoundException
: 참조하려는 파일 명이 지정된 위치에 존재하지 않는 경우 발생합니다.ClassNotFoundException
: 클래스가 로드되지 않는 경우 발생합니다.InterruptedException
: non-runnable 상태인 thread를 Runnable하게 만들 수 있도록 사용 할 수 있습니다.
try-catch
- try 블록에는 예외가 발생할 가능성이 있는 코드를 작성하고 예외가 발생한다면 catch 블록이 수행됩니다.
try{ 예외가 발생할 수 있는 코드 영역 } catch(예외 타입 e) { 예외 처리 코드 영역 }
- 예시
int[] arr = {1, 2, 3, 4, 5}; try { for(int i=0;i<6;i++){ System.out.println(arr[i]); } } catch (ArrayIndexOutofBoundsException e) { System.out.println(e.getMessage()); // 에러 메시지 System.out.println(e.toString()); // 에러 명 + 에러 메시지 e.printStackTrace(); // 어디서 에러가 났는지 추적할 수 있게 해준다. } System.out.println("여기는?");
가장 아래 "여기는?" 은 try 내부에서 발생한 예외를 catch문을 통해 잘 핸들링해줬을 경우 출력됩니다. 만약, 위의 try블럭 내부 코드를 try-catch 문을 통해 작성하지 않았다면, 가장 아래 "여기는?"은 출력되지 않습니다.
try-catch-finally
- finally 블럭에서 파일을 닫거나 네트웍을 닫는 등의 리소스 해제를 구현해줍니다.
- try 블럭이 수행되는 경우 finally 블럭은 항상 수행됩니다.
FileInputStream fis = null; try{ fis = new FileInputStream("a.txt"); System.out.println("1"); } catch(FileNotFoundException e) { e.printStackTrace(); } finally { if(fis != null){ try{ fis.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("2"); } System.out.println("3");
위와 같은 경우 a.txt 파일이 없다면 1이 출력되지 않고 바로 catch로 넘어가게 됩니다.
catch 블럭 내부에서 예외에 대한 추적 결과를 출력합니다.
그 다음, finally로 이동하여 리소스에 대한 핸들링을 해줍니다. 만약 파일이 열린 다음 예외가 발생했다면 파일을 해제해주고, 열리기 전에 예외가 발생했다면 그냥 넘어가고 2가 출력됩니다.
이 후 예외에 대한 핸들리을 했다면 3이 출력 됩니다.
try-with-resource
- 리소스를 사용하는 경우 close() 하지 않아도 자동으로 해제 되도록 합니다.
- 자바 7부터 제공되는 구문입니다.
- 리소스를 try 내부에서 선언해주어야 합니다.
- close()를 명시적으로 호출하지 않아도 try{} 블록에서 열린 리소스는 정상적인 경우나 예외가 발생한 경우 모두 자동으로 해제됩니다.
- 해당 리소스 클래스가 AutoCloseable 인터페이스를 구현해야 합니다.
- FileInputStream의 경우 AutoCloseable을 구현하고 있습니다.
- 자바 9부터는 리소스는 try{} 외부에서 선언하고 변수만을 try(obj) 와 같이 사용할 수 있습니다.
static class AutoClose implements AutoCloseable { FileInputStream fs = null; @Override public void close() throws Exception { if(fs!=null){ try{ fs.close(); } catch(IOException e){ System.out.println("error handle"); } } System.out.println("finish"); } } AutoClose ac = new AutoClose(); try(ac){ throw new Exception(); } catch(Exception e){ System.out.println(e); }
위의 예시와 같은 경우 리소스 자동 해제를 구현한 AutoClose 클래스에 대한 try-with-resource 예시입니다.
가장 아래 try블럭 내부에서 예외가 발생한다면 자동으로 ac에 구현된 close메서드가 실행되게 됩니다. 그 다음 catch블럭에 구현된 예외가 출력되게 됩니다.
그러므로 위의 출력 결과는 finish, java.lang.Exception 입니다.
예외 처리 미루기
- 예외 처리는 예외가 발생하는 문장에서 try-catch 블록으로 처리하는 방법과 이를 사용하는 부분에서 처리하는 방법 두 가지가 있습니다.
- throws를 이용하면 예외가 발생하는 부분을 사용하는 문장에서 예외를 처리할 수 있습니다.
method() throws FileNotFoundException { FileInputStream fis = new FileInputStream(filename); // FileNotFoundException 예외 발생 가능 } try { method(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); }
위와 같은 경우
method()
를 호출했을 때 FileNotException이 발생한다면, try 구문 내에서 알아서 catch 블럭으로 이동하게 됩니다.만약, 원하는 예외에 대해 특정하기 힘들다면 모든 예외의 상위 클래스인 Exception을 통해 핸들링 할 수도 있습니다. (Exception 클래스를 통해 default 처리를 할 경우 Exception예외에 대한 catch 블럭은 가장 마지막에 위치해야 합니다.
여러 개의 예외 핸들링
여러 개의 예외를 다음과 같이 한 번에 핸들링 할 수 있습니다.
try { FileInputStream fis = new FileInputStream("a.txt"); } catch (FileNotFoundException | IOException){ e.printStackTrace(); }
사용자 정의 예외 클래스
- 자바에서 제공되는 예외 클래스 외에 직접 만들어야 하는 예외를 정의 할 수 있습니다.
- 기존 예외 클래스 중 가장 유사한 예외 클래스에서 상속 받아 사용자 정의 클래스를 만듭니다.
- 예시)
class PasswordException extends IllegalArgumentException{ public PasswordException(String message){ super(message); } } class Password { String password; setPassword(String password) throws PasswordException { if(password==null){ throw new PasswordException("패스워드는 null일 수 없습니다."); } else if(password.length() < 5) { throw new PasswordException("패스워드의 길이는 5 이상입니다."); } this.password = password; } } Password password = new Password(); try{ //try-catch문1 password.setPassword(null); } catch(PasswordException e){ System.out.println(e); } try{ //try-catch문2 password.setPassword("1234"); } catch(PasswordException e){ System.out.println(e); } try{ //try-catch문3 password.setPassword("12345"); } catch(PasswordException e){ System.out.println(e); }
위와 같이
PasswordException
을 정의하여 사용자 정의 예외 처리를 할 수 있습니다.사용자 정의 예외가 발생하는 구간에서 예외를 throw해주면 이를 호출하는 구간에서 try-catch를 통해 처리해줍니다.
try-catch 문1과 try-catch 문2는 예외가 발생함으로 catch문을 통과하게 되고 try-catch 문3은 throw된 예외가 없으므로 catch블럭을 실행하지 않습니다.
logging
- 시스템 운영에 대한 기록을 할 수 있습니다.
- 오류가 발생할 경우 오류에 대한 기록을 남겨 디버깅에 용이할 수 있습니다.
- 로그 파일로 기록하여 필요한 정보를 남길 수 있습니다.
- 너무 적은 로그는 정확한 시스템 정보를 파악하기 어렵고, 너무 많은 로그는 빈번한 file I/O가 발생하도록 해 오버헤드와 로그 파일의 백업에 문제가 생길 수 있습니다.
java.util.logging
- 자바에서 기본적으로 제공하는 log package입니다.
- 파일이나 콘솔에 로그 내용을 출력할 수 있습니다.
- 설정 파일을 편집하여 로그의 출력 방식이나 로그 레벨을 변경 할 수 있습니다.
- logging 패키지에서 제공하는 로그 레벨은 severe, warning, info, config, fine, finer, finest 가 있습니다.
- 오픈 소스로는 log4j를 많이 사용합니다.
Logger logger = Logger.getLogger("mylogger"); FileHandler logFile = FileHandler("log.txt", true); logFile.setFormatter(new SimpleFormatter()); logFile.setLevel(Level.ALL); // 로그 레벨 설정 logger.addHandler(logFile); // logger에 logFile 추가 ... logger.finest(msg); //finest 레벨의 로거를 통해 로그를 파일에 작성
오늘은 스트림 연산과 예외처리에 대해 알아보았습니다. 스트림 연산과 같은 경우 자바스크립트의 고차연산과 닮은 점이 많아 이해하는데 어렵지는 않았습니다. 스트림을 통한 연산이 어떻게 보면 코드를 간단하게 만들어줄 수는 있지만, 중간 연산의 개수가 많아진다면 이해하는데 어려움이 있을 것 같다는 생각이 들었습니다. 하지만, 객체 배열에서 원하는 멤버 변수에 대한 합을 구하거나 모든 배열 각 원소에 원하는 연산을 하고 싶을 경우 스트림을 사용한다면 훨씬 효율적으로 코드를 작성할 수 있을 것 같다는 생각이 들었습니다.
예외 처리와 같은 경우 모든 언어를 배울 때 필수적으로 배워야되는 부분이라고 생각합니다. try catch, throws를 통해 예외를 처리해줌으로써 안정적인 프로그램을 만들 수 있다고 생각합니다. 또한, 에러 로깅이 남긴 시스템 정보에 대한 로그를 통해 디버깅하여 에러를 해결 하는 것이 예외를 빠르게 처리 할 수 있는 방법이 될 것 같습니다.
반응형LIST'언어 > Java' 카테고리의 다른 글
Lombok 살펴보기 (0) 2023.08.16 자바 데코레이터 패턴, 쓰레드, 멀티 쓰레드 프로그래밍 (2) 2023.07.16 자바 내부 클래스, 람다식, 함수형 프로그래밍, 함수형 인터페이스 (2) 2023.07.13 자바 자료구조, 제네릭, 컬렉션 (0) 2023.07.11 자바 Object, Class (5) 2023.07.10 - 자료의 대상과 관계없이 동일한 연산을 수행합니다.