프로그램의 비정상 종료를 피하여 시스템이 원할이 실행되도록 함
실행 오류가 발생한 경우 오류의 과정을 재현하는 것은 현실적으로 힘들다
오류가 발생한 경우 log를 남겨서 추후 log 분석을 통해 그 원인을 파악하여 bug를 수정하는 것이 중요
시스템 오류(error) : 가상 머신에서 발생, 프로그래머가 처리 할 수 없는 오류임
ex) 동적 메모리가 없는 경우, 스택 메모리 오버플로우등
예외(Exception) :프로그램에서 제어 할 수 있는 오류
ex) 읽어들이려는 파일이 존재하지 않거나, 네트웍이나 DB연결이 안되는 경우등
자바는 안전성이 중요한 언어로 대부분 프로그램에서 발생하는 오류에 대해 문법적으로 예외 처리를 해야함
모든 예외 클래스의 최상위 클래스는 Exception 클래스
자바에서는 다양한 예외들에 대해 그 처리를 위한 클래스가 제공되고 있음
Arithmetic Exception :정수를 0 으로 나눈 경우 발생
NullPointerException : 초기화 되지 않은 Object를 사용하는 경우
ArrayIndexOutOfBoundsException :배열의 크기를 넘어선 위치를 참조하려는 경우
FileNotFoundException :참조하는 파일이 지정된 위치에 존재하지 않는 경우
ClassNotFoundException : 클래스가 로드되지 않은 경우
try{
예외가 발생할 수 있는 코드 부분
}catch(처리할 예외 타입 e){
try블록 안에서 예외가 발생했을 때 예외처리하는 부분
}
public class ArrayExceptionHandling {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
try{
for(int i=0; i<=5; i++){
System.out.println(arr[i]);
}
}catch(ArrayIndexOutOfBoundsException e){ // arr[5]에서 예외 발생
System.out.println(e);
}
System.out.println("비정상 종료되지 않았습니다.");
}
}
finally 블럭에서 파일를 닫거나 네트웍을 닫는 등의 리소스 해제 구현을 함
try{} 블럭이 수행되는 경우, finally{} 블럭은 항상 수행 됨
여러 개의 예외 블럭이 있는 경우 각각에서 리소스를 해제하지 않고 finally 블록에서 해제하도록 구현함
컴파일러에 의해 예외가 처리 되는 예 (파일 에러 처리)
public class FileExceptionHandling {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("a.txt");
} catch (FileNotFoundException e) {
System.out.println(e);
//return;
}finally{
if(fis != null){
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("항상 수행 됩니다.");
}
System.out.println("여기도 수행됩니다.");
}
}
리소스를 사용하는 경우 close() 하지 않아도 자동으로 해제 되도록 함
자바 7부터 제공되는 구문
리소스를 try() 내부에서 선언해야만 함
close()를 명시적으로 호출하지 않아도 try{}블록에서 열린 리소스는 정상적인 경우나 예외가 발생한 경우 모두 자동으로 해제됨
해당 리소스 클래스가 AutoCloseable 인터페이스를 구현 해야 함
FileInputStream의 경우에는 AutoCloseable을 구현하고 있음
자바 9 부터 리소스는 try() 외부에서 선언하고 변수만을 try(obj) 와 같이 사용할 수 있음
public class AutoCloseObj implements AutoCloseable{
@Override
public void close() throws Exception {
System.out.println("리소스가 close() 되었습니다");
}
}
public class AutoCloseTest {
public static void main(String[] args) {
AutoCloseObj obj = new AutoCloseObj();
try (obj){
throw new Exception();
}catch(Exception e) {
System.out.println("예외 부분 입니다");
}
}
}
예외 처리는 예외가 발생하는 문장에서 try-catch 블록으로 처리하는 방법과 이를 사용하는 부분에서 처리하는 방법 두 가지가 있음
throws를 이용하면 예외가 발생할 수 있는 부분을 사용하는 문장에서 예외를 처리할 수 있음
예제
public class ThrowsException {
public Class loadClass(String fileName, String className) throws FileNotFoundException, ClassNotFoundException{
FileInputStream fis = new FileInputStream(fileName); //FileNotFoundException 발생
Class c = Class.forName(className); //ClassNotFoundException 발생
return c;
}
public static void main(String[] args) {
ThrowsException test = new ThrowsException();
try {
test.loadClass("a.txt", "java.lang.String");
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
}
}
try {
test.loadClass("a.txt", "java.lang.String");
} catch (FileNotFoundException | ClassNotFoundException e) {
e.printStackTrace();
}
try {
test.loadClass("a.txt", "java.lang.String");
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
자바에서 제공되는 예외 클래스외에 프로그래머가 직접 만들어야 하는 예외가 있을 수 있음
기존 예외 클래스중 가장 유사한 예외 클래스에서 상속 받아 사용자 정의 예외 클래스를 만든다.
기본적으로 Exception 클래스를 상속해서 만들 수 있음
public class PasswordException extends IllegalArgumentException{
public PasswordException(String message) {
super(message);
}
}
public class PasswordTest {
private String password;
public String getPassword(){
return password;
}
public void setPassword(String password) throws PasswordException{
if(password == null){
throw new PasswordException("비밀번호는 null 일 수 없습니다");
}
else if( password.length() < 5){
throw new PasswordException("비밀번호는 5자 이상이어야 합니다.");
}
else if (password.matches("[a-zA-Z]+")){
throw new PasswordException("비밀번호는 숫자나 특수문자를 포함해야 합니다.");
}
this.password = password;
}
public static void main(String[] args) {
PasswordTest test = new PasswordTest();
String password = null;
try {
test.setPassword(password);
System.out.println("오류 없음1");
} catch (PasswordException e) {
System.out.println(e.getMessage());
}
password = "abcd";
try {
test.setPassword(password);
System.out.println("오류 없음2");
} catch (PasswordException e) {
System.out.println(e.getMessage());
}
password = "abcde";
try {
test.setPassword(password);
System.out.println("오류 없음3");
} catch (PasswordException e) {
System.out.println(e.getMessage());
}
password = "abcde#1";
try {
test.setPassword(password);
System.out.println("오류 없음4");
} catch (PasswordException e) {
System.out.println(e.getMessage());
}
}
}
시스템 운영에 대한 기록
오류가 발생 했을 때 그 오류에 대한 기록을 남겨 디버깅을 용이하게 함
로그 파일에 기록하는 코드를 추가하여 필요한 정보가 로그로 남을 수 있도록 한다
디버깅, 시스템 에러 추적, 성능, 문제점 향상들을 위해 사용
어느정도까지 로그를 남길 것인가?
자바에서 기본적으로 제공되는 log package
파일이나 콘솔에 로그 내용을 출력할 수 있음
jre/lib/logging.properties 파일을 편집하여 로그의 출력방식 로그 레벨을 변경 할 수 있음
logging 패키지에서 제공하는 로그 레벨은 severe, warning, info, config, fine, finer, finest 임
오픈소스로는 log4j를 많이 사용하고 있음
public class MyLogger {
Logger logger = Logger.getLogger("mylogger");
private static MyLogger instance = new MyLogger();
public static final String errorLog = "log.txt";
public static final String warningLog = "warning.txt";
public static final String fineLog = "fine.txt";
// 로그를 남기기 위한 FileHandler 생성
private FileHandler logFile = null;
private FileHandler warningFile = null;
private FileHandler fineFile = null;
private MyLogger(){
try {
logFile = new FileHandler(errorLog, true);
warningFile = new FileHandler(warningLog, true);
fineFile = new FileHandler(fineLog, true);
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logFile.setFormatter(new SimpleFormatter());
warningFile.setFormatter(new SimpleFormatter());
fineFile.setFormatter(new SimpleFormatter());
//FileHandler의 레벨 설정
logger.setLevel(Level.ALL);
fineFile.setLevel(Level.FINE);
warningFile.setLevel(Level.WARNING);
logger.addHandler(logFile);
logger.addHandler(warningFile);
logger.addHandler(fineFile);
}
public static MyLogger getLogger(){
return instance;
}
public void log(String msg){
logger.finest(msg);
logger.finer(msg);
logger.fine(msg);
logger.config(msg);
logger.info(msg);
logger.warning(msg);
logger.severe(msg);
}
public void fine(String msg){
logger.fine(msg);
}
public void warning(String msg){
logger.warning(msg);
}
}
public class StudentNameFormatException extends IllegalArgumentException{
public StudentNameFormatException(String message){
super(message);
}
}
public class Student {
private String studentName;
MyLogger myLogger = MyLogger.getLogger();
public Student(String studentName){
if(studentName == null){
throw new StudentNameFormatException("name must not be null");
}
if( studentName.split(" ").length > 3)
throw new StudentNameFormatException("이름이 너무 길어요");
this.studentName = studentName;
}
public String getStudentName() {
myLogger.fine("begin getStudentName()");
return studentName;
}
}
public class StudentTest {
public static void main(String[] args) {
MyLogger myLogger = MyLogger.getLogger(); // 1. Logger인스턴스 생성
String name = null;
try{
Student student = new Student(name);
}catch( StudentNameFormatException e ){
myLogger.warning(e.getMessage());
}
try{
Student student = new Student("Edward Jon Kim Test");
}catch ( StudentNameFormatException e){
myLogger.warning(e.getMessage());
}
Student student = new Student("James");
}
}