본문 바로가기

STUDY/국비과정

[JAVA 웹 개발 공부] 국비지원 22일차 - 에러, 예외, 오류, 래퍼 클래스, 예외 처리, try-catch문, GUI, JFrame, JPanel

프로그램 오류

 

프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료되는 경우가 있다. 이러한 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 한다.

이를 발생시점에 따라 '컴파일 에러'와 '런타임 에러'로 나눌 수 있는데, 글자 그대로 '컴파일 에러'는 컴파일 할 때 발생하는 에러이고 프로그램의 실행도중에 발생하는 에러를 '런타임 에러'라고 한다. 이 외에도 '논리적 에러'가 있는데, 컴파일도 잘되고 실행도 잘되지만 의도한 것과 다르게 동작하는 것을 말한다.

 

*컴파일 에러 : 컴파일 시에 발생하는 에러

*런타임 에러 : 실행 시에 발생하는 에러

*논리적 에러 : 실행은 되지만, 의도와 다르게 동작하는 것

 

자바에서는 실행 시(runtime) 발생할 수 있는 프로그램 오류를 '에러'와 '예외' 두 가지로 구분하였다.

에러는 메모리 부족이나 스택오버플로우와 같이 일단 발생하면 복구할 수 없는 심각한 오류이고, 예외는 발생하더라도 수습될 수 있는 비교적 덜 심각한 것이다.

에러가 발생하면, 프로그램의 비정상적인 종료를 막을 길이 없지만, 예외는 발생하더라도 프로그래머가 이에 대한 적절한 코드를 미리 작성해 놓음으로써 프로그램의 비정상적인 종료를 막을 수 있다.

 

 

 

 

래퍼(wrapper) 클래스

 

객체지향 개념에서 모든 것은 객체로 다루어져야 한다. 그러나 자바에서는 8개의 기본형을 객체로 다루지 않는데 이것이 바로 자바가 완전한 객체지향 언어가 아니라는 얘기를 듣는 이유이다. 그 대신 보다 높은 성능을 얻을 수 있었다.

때로는 기본형(primitive type) 변수도 어쩔수 없이 객체로 다뤄야 하는 경우가 있다. 이 때 사용되는 것이 래퍼클래스이다.

 

*기본형과 래퍼클래스

기본형 래퍼클래스
boolean Boolean
char Character
byte Byte
short Short
int Integer
long Long
float Float
double Double

 

래퍼 클래스들은 모두 equals()가 오버라이딩되어 있어서 주소값이 아닌 객체가 가지고 있는 값을 비교한다. 그래서 실행결과를 보면 equals()를 이용한 두 Integer객체의 비교 결과가 true라는 것을 알 수 있다. 오토박싱이 된다고 해도 Integer객체에 비교연산자를 사용할 수 없다. 대신 compareTo()를 제공한다. 

그리고 toString()도 오버라이딩되어 있어서 객체가 가지고 있는 값을 문자열로 변환하여 반환한다. 

이 외에도 래퍼 클래스들은 MAX_VALUE, MIN_VALUE, SIZE, BYTES, TYPE 등의 static 상수를 공통적으로 가지고 있다.

 

*문자열을 숫자로 변환하기

문자열 → 기본형 문자열 → 래퍼 클래스
byte b = Byte.parseByte("100"); Byte b = Byte.valueOf("100");
short s = Short.parseShort("100"); Short s = Short.valueOf("100");
int i = Integer.parseInt("100"); Integer i = Integer.valueOf("100");
long l = Long.parseLong("100"); Long l = Long.valueOf("100");
float f = Float.parseFloat("3.14"); Float f = Float.valueOf("3.14");
double d = Double.parseDouble("3.14"); Double d = Double.valueOf("3.14");

 

JDK1.5부터 도입된 '오토박싱(autoboxing)' 기능 때문에 반환값이 기본형일 때와 래퍼 클래스일 때의 차이가 없어졌다.

그래서 그냥 구별없이 valueOf()를 쓰는 것도 괜찮은 방법이다. 

int i = Integer.parseInt("100");
int i = Integer.valueOf("100");

 

*오토박싱(autoboxing) : 기본형 값을 래퍼 클래스의 객체로 자동 변환해주는것

*언박싱(unboxing) : 래퍼 클래스의 객체를 기본형 값으로 변환해주는 것

int primitive = 100;

Integer ref = Integer.valueOf(primitive); // boxing
int unbox = ref.intValue(); // un-boxing

Integer ref2 = primitive; // auto-boxing
int unbox2 = ref2;

 

 

 

예외 처리

 

프로그램의 실행도중에 발생하는 에러는 어쩔 수 없지만, 예외는 프로그래머가 이에 대한 처리를 미리 해주어야 한다.

예외처리(exception handling)란, 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것이며, 예외처리의 목적은 예외의 발생으로 인한 실행 중인 프로그램의 갑작스런 비정상 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 하는 것이다.

발생한 예외를 처리하지 못하면, 프로그램은 비정상적으로 종료되며, 처리되지 못한 예외는 JVM의 '예외처리기'가 받아서 예외의 원인을 화면에 출력한다.

 

ex) 예외 처리하기

// 은행계좌
// 잔고

// throws 자유이름예외.. 왜 입금, 출금, 이체가 안되는지
// 입금하기(금액) 
// 출금하기(금액) 
// 이체하기(은행계좌, 금액) 

class NegativeIntegerException extends RuntimeException {
	public NegativeIntegerException(String message) {
		super(message);
	}
}

public class BankAccount {
	private int money;
	
	public boolean deposit(int money) {
		if (money > 0) {
			this.money += money;
			return true;
		} else {
			throw new NegativeIntegerException("음수의 금액을 입금할 수 없습니다.");
		}
	}
	
	public boolean withdraw(int money) {
		if (this.money >= money) {
			this.money -= money;
			return true;
		} else {
			throw new NegativeIntegerException("잔고가 부족합니다. 부족한 금액 " + (money - this.money));
		}
	}
	
	public void transfer(BankAccount another, int money) {
		if (another == null) {
			throw new NullPointerException("상대 계좌 참조가 전달되지 않았습니다.");
		}
		if (money > 0) {
			withdraw(money);
			another.deposit(money);
		}
	}
	
	
	public static void main(String[] args) {
		BankAccount account = new BankAccount();
		account.deposit(9000);
		account.withdraw(4000);
		
		BankAccount account2 = new BankAccount();
		
		account.transfer(null, 10000);
		System.out.println(account.money);
		System.out.println(account2.money);
	}
}
Exception in thread "main" NegativeIntegerException: 음수의 금액을 입금할 수 없습니다.
	at BankAccount.deposit(BankAccount.java:30)
	at BankAccount.main(BankAccount.java:45)

 

 

 

try-catch문

 

예외를 처리하기 위해서는 try-catch문을 사용하며, 그 구조는 다음과 같다.

try {
	// 예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch (Exception1 e1) {
	// Exception1이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
} catch (Exception2 e2) {
	// Exception2가 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
} catch (ExceptionN eN) {
	// ExceptionN이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
}

 

하나의 try 블럭 다음에는 여러 종류의 예외를 처리할 수 있도록 하나 이상의 catch 블럭이 올 수 있으며, 이 중 발생한 예외의 종류와 일치하는 단 한 개의 catch 블럭만 수행된다.

발생한 예외의 종류와 일치하는 catch 블럭이 없으면 예외는 처리되지 않는다.

 

*try-catch문에서의 흐름

[try 블럭 내에서 예외가 발생한 경우]
→ 발생한 예외와 일치하는 catch 블럭이 있는지 확인한다.
→ 일치하는 catch 블럭을 찾게 되면, 그 catch 블럭 내의 문장들을 수행하고 
전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다.
만일 일치하는 catch 블럭을 찾지 못하면, 예외는 처리되지 못한다.

[try 블럭 내에서 예외가 발생하지 않은 경우]
→ catch 블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.

 

 

 

자바 GUI(graphic user interface)

 

GUI(Graphic User Interface) 프로그램이란 그래픽 환경에서 사용자와 상호 작용을 통해 실행되는 프로그램을 의미한다.

그래픽 환경에서 상호작용을 제공하는 프로그램을 GUI 프로그램이라 한다.

현재 대부분의 프로그램은 버튼이나 스크롤바와 같은 그래픽 사용자 인터페이스(Graphic User Interface)를 사용한다.

GUI는 객체들로 만들어진다. 이들 객체들을 GUI 컴포넌트(component)라고 한다. 대표적인 GUI 컴포넌트가 버튼, 체크박스, 콤보박스이다. 위젯(widget) 또는 컨트롤(control)이라고도 한다.

 

현재 자바에서 사용할 수 있는 GUI에는 AWT(Abstract Window Toolkit), 스윙(Swing), JavaFx가 있다. 

AWT는 초기 자바 버전에서 제공하였던 GUI이다. AWT는 운영체제가 제공하는 자원을 이용하여서 컴포넌트를 생성한다.

스윙은 AWT와 달리, 순수 자바로 작성되어 있기 때문에, 어떤 플랫폼에서도 일관된 화면을 보여줄 수 있다.

최근에 발표된 JavaFx는 RIA(Rich Internet Application) 시장과 모바일 시장을 겨냥하고 만들어진 GUI이다.

 

*AWT와 스윙 비교

자바 AWT 자바 스윙
AWT 컴포넌트는 플랫폼에 따라 다르게 보인다. 스윙의 컴포넌트는 자바로 작성되어서 플랫폼에 독립적이다.
AWT 컴포넌트는 무겁다(중량). 스윙 컴포넌트는 가볍다(경량).
AWT는 룩앤필을 지원하지 않는다. 스윙은 룩앤필을 지원한다.
AWT는 컴포넌트의 개수가 적다. 스윙은 비교적 많은 컴포넌트를 제공한다.
AWT는 MVC(모델 뷰 컨트롤러) 모델을 따르지 않는다. 스윙은 MVC 모델을 따른다.

 

자바에서 AWT와 스윙의 이름 충돌로 인해 스윙에 속하는 클래스 이름 앞에 J를 붙이기로 하였다. 

예를 들어 버튼을 나타내는 클래스가 AWT에서는 Button이고 스윙에서는 JButton이다.

 

 

 

GUI 기초

 

1. 컨테이너 컴포넌트

자바가 제공하는 컴포넌트는 크게 단순 컴포넌트와 컨테이너 컴포넌트로 나누어진다.

컨테이너 컴포넌트란 다른 컴포넌트들을 내부에 넣을 수 있는 컴포넌트를 의미한다.

GUI 화면은 먼저 컨테이너를 만들고 그 안에 자신이 필요한 컴포넌트를 넣어서 작성하게 된다.

 

컴포넌트 종류 설명
단순 컴포넌트 단순한 컴포넌트로서 JButton, JLabel, JCheckbox, JChoice, JMenu, JTextField, JScrollbar, JTextArea, JCanvas 등이 여기에 속한다.
컨테이너 컴포넌트 다른 컴포넌트를 안에 포함할 수 있는 컴포넌트로서 JFrame, JDialog, JPanel, JScrollPane 등이 여기에 속한다.

 

2. 최상위 컨테이너

컨테이너는 다시 최상위 컨테이너와 일반적인 컨테이너로 나누어진다. 

최상위 컨테이너란 절대 다른 컨테이너 안에 포함될 수 없는 컨테이너를 의미한다.

JFrame, JDialog, JApplet 등이 여기에 해당된다.

 

 

3. 프레임 생성하기

(1) 스윙의 JFrame을 사용하기 위해 import문으로 클래스를 포함한다. 

(2) new 연산자를 이용해서 JFrame 객체를 생성한다. 

(3) 프레임 객체의 setSize() 메소드를 호출하여 프레임의 크기를 변경한다.

(4) 프레임 객체의 setVisible() 메소드를 호출하여 프레임이 화면에 나타나게 한다.

(5) 프레임의 닫힘 버튼을 누르면 전체 프로그램이 종료되게 한다.

import javax.swing.JFrame;

public class Main {
	public static void main(String[] args) {
		JFrame frame = new JFrame("처음 만든 창");
		frame.setSize(500, 500);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
}

 

 

 

JFrame 클래스

 

JFrame은 프레임(Frame), 메뉴바(Menubar), 컨텐트 페인(Content pane)으로 되어 있다.

Frame은 AWT에서 제공하는 클래스(java.awt.Frame)로서 윈도우 자체를 나타낸다.

메뉴바는 메뉴를 부착할 수 있는 공간이다.

컨텐트 페인은 컴포넌트들을 부착하는 판유리 같은 것이다. 페인(pane)은 한 장의 판유리라는 의미이다.

 

*자주 사용하는 메소드

메소드 설명
add(component) 프레임의 컨텐트 페인에 컴포넌트를 추가한다.
setLocation(x, y) 프레임의 위치를 설정한다.
setSize(width, height) 프레임의 크기를 설정한다.
setIconImage(IconImage) 윈도우의 제목줄에 표시할 아이콘을 설정한다.
setTitle() 제목줄의 제목을 변경한다.
setResizable(boolean) 사용자가 윈도우의 크기를 조절하도록 허용하는지 여부를 설정한다.
getContentPane() 프레임 안의 컨텐트 페인을 가져온다.
setLayout() 프레임의 배치 관리자를 설정한다.
setDefaultCloseOperation() 사용자가 프레임을 닫을 때 실행되는 동작을 지정한다.
일반적으로 JFrame.EXIT_ON_CLOSE로 지정한다.

 

 

 

JPanel 클래스

 

패널(panel)은 컴포넌트들을 부착할 수 있도록 설계된 컨테이너 중의 하나이다(최상위 컨테이너는 아니다).

스윙에서는 JPanel이란 이름으로 제공된다.

패널을 쓰지 않고 프레임에 컴포넌트들을 직접 추가할 수 있지만 일반적으로 패널에 컴포넌트를 추가하고 그 패널을 프레임에 추가한다. 패널을 쓰는 것이 유지 보수 및 배치 관리에 유리한 경우가 많다.

 

*자주 사용하는 메소드

메소드 설명
add(aComponent) 패널에 컴포넌트를 추가한다.
remove(aComponet) 패널에 컴포넌트를 삭제한다.
setBackground(Color c) 패널에 배경색을 변경한다.