1학년 때 JAVA를 처음 배우고, 2학년 이후로는 JAVA를 쓸 일이 거의 없었던 것 같다. (그래도 1학년 때 나름 JAVA 공부 열심히 해뒀음 ㅎ) 현업에서는 JAVA를 많이 사용한다고 하니,,, SSAFY 입과 전에 열심히 복습해 보겠어 😎 (if, for, while 문 같은 내용은 정리하지 않았다! java 적인 것? 위주로 정리했다.)
JAVA
- 객체 지향적 언어 (↔ 절차 지향적 언어)
- write once run anywhere (동일한 프로그램이 운영체제 가리지 않고 실행)
* WROA - 현대에는 자바뿐만 아니라 대부분의 프로그래밍 언어가 이를 일부분에서 모두 지원
객체지향 프로그래밍
- Object-Oriented Programming
- 로직을 상태(state)와 행위(behave)로 이루어진 객체로 만드는 것
- 객체들을 마치 레고 블럭처럼 조립해서 하나의 프로그램을 만드는 것 = 객체지향 프로그래밍
- 복잡함 속에서 필요한 관점만을 추출하는 행위 = 추상화
- 부품화 → 효율성 ↑, 문제 발생 원인 파악 및 해결이 쉬워짐 (부품화의 기준은 추상화?!)
- 부품화보다 중요한 것은 적절함! 그래서 설계가 어려운 것,,
- 연관된 메소드와 그 메소드가 사용하는 변수들을 분류하고 그룹핑하는 것이 핵심
- 내부의 동작 방법을 단단한 케이스 안으로 숨기고 사용자에게는 그 부품의 사용방법만을 노출 = 은닉화, 캡슐화
- 부품들 간의 약속 = 인터페이스
환경 마련하기
나는 mac을 사용해서 모두 macOS 용으로 다운 받았다.
# 설치된 jdk 확인
cd /Library/Java/JavaVirtualMachines
# 경로 이동
cd (설치된jdk이름).jdk/Contents/Home
# 반환 받은 경로 복사
pwd
# bash_profile 파일을 열고
vi ~/.bash_profile
# JAVA_HOME 지정 후 저장
export JAVA_HOME = /Library/Java/JavaVirtualMachines/jdk1.8.0_351.jdk/Contents/Home
# java 버전 확인
java -version
3. Project 생성 → Package 생성 → Class 생성
* 동작 원리 : 코드 작성(.java) → 컴파일 (.class) → 실행
자료형
1. 정수형
데이터 타입 | 메모리 크기 | 표현 가능 범위 |
byte | 1 byte | -128 ~ 127 |
short | 2 byte | -32,768 ~ 32,767 |
int | 4 byte | -2,147,483,648~2,147,483,647 |
long | 8 byte | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
int a = 2147483648; // 범위를 벗어나서 오류 발생
long a = 2147483648; // 오류 발생
long a = 2147483648L; // long type 임을 명시 (byte, short의 경우 명시할 필요 x)
2. 실수형
데이터 타입 | 메모리 크기 | 표현 가능 범위 |
float | 4byte | ±(1.40129846432481707e-45 ~ 3.40282346638528860e+38) |
double | 8byte | ±(4.94065645841246544e-324d ~ 1.79769313486231570e+308d) |
float a = 1.1; // 오류 발생
float a = 1.1F; // float type 임을 명시
double a = 1.1; // 정상 (자바에서 실수형 상수는 double type)
3. 문자
데이터 타입 | 메모리 크기 | 표현 가능 범위 |
char | 2byte | 모든 유니코드 문자 |
4. 문자열 - String
String a = "Hello world";
String b = new String("Hello world");
System.out.println(a == b); // false (두 데이터 타입이 동일한 객체인지)
System.out.println(a.equals(b)); // true (문자열 비교 시 .equals를 사용)
주석
- 로직에 대한 설명이나 코드를 비활성화 할 때 사용
- 프로그래밍적으로 해석되지 않음
// 한줄 주석
/*
여려줄
주석
*/
형 변환
1. 자동(암시적) 형 변환
: 표현범위가 좁은 데이터 타입에서 넓은 데이터 타입으로의 변환만 허용
double a = 3.0F; // possible
float a = 3.0; // impossible
2. 명시적 형 변환
: 자동 형 변환이 적용되지 않는 경우에는 수동으로 형 변환
// (데이터 type) 데이터 값
float a = 100.0;
int b = 100.0F;
float a = (float)100.0;
int b = (int)100.0F;
* 프로모션 캐스팅 - 큰 자료형에 작은 자료형 대입 (자동)
* 디모션 캐스팅 - 작은 자료형에 큰 자료형 대입 (수동)
배열
// 배열 선언 및 생성
// 타입[] 배열이름 = new 타입[배열길이] ;
int[] arr = new int[4];
String[] food = { "마라탕", "닭강정", "김밥" };
// 1과 2의 결과는 같음 !
for (int i = 0; i < food.length; i++) { // 1
System.out.println(food[i] + "을 추천합니다.");
}
for (String f : food) { // 2 (자바 5.0부터 도입된 기능)
System.out.println(f + "을 추천합니다.");
}
메소드
public class MethodDemo4 {
public static void counting(int limit) { // method 정의 (반환 값 없음 - void)
int i = 0;
while (i++ < limit) { // 1~limit 까치 출력
System.out.println(i);
}
}
public static void main(String[] args) {
counting(5); // method 호출
}
}
입출력
import java.util.Scanner;
// input
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
// file 에서 읽기
try {
File file = new File("out.txt"); // if, 2
Scanner scan = new Scanner(file);
while(scan.hasNextInt()) { // 입력값이 생기기 전까지 실행을 유보
System.out.println(scan.nextInt()*10); // then, 20
}
scan.close();
} catch(FileNotFoundException e){
e.printStackTrace();
}
// output
System.out.println(num);
클래스, 인스턴스, 객체
클래스
- 객체를 정의하고 만들어 내기 위한 틀
- 연관되어 있는 변수와 메소드의 집합 (상태를 나타내는 field와 행동을 나타내는 method로 구성)
- 클래스 선언 : 접근제어자 class 클래스이름
public class Robot {
// field (클래스 변수)
int battery;
// method
public void increase_battery(){
battery++;
}
public void decrease_battery(){
battery--;
}
}
객체
- 소프트웨어 세계에 구현할 대상
- 클래스에 선언된 모양 그대로 생성된 실체
- 클래스의 인스턴스
인스턴스
- 객체를 소프트웨어에 실체화 한 것
- 실체화된 인스턴스는 메모리에 할당됨
- oop의 관점에서 객체가 메모리에 할당되어 실제 사용될 때 '인스턴스'라고 부름
- 클래스이름 객체참조변수 = new 생성자();
* 생성자 : 클래스와 이름이 같은 특별한 method로, 주로 객체 field 초기화 시 이용!
* this : 클래스를 통해 만들어진 인스턴스 자신을 가리킴
//참조 변수 선언과 인스턴스 생성 동시에
Robot r1 = new Robot();
클래스 멤버와 인스턴스 멤버
- 인스턴스 메소드는 클래스 맴버에 접근 할 수 있음
- 클래스 메소드는 인스턴스 맴버에 접근 할 수 없음 ( = 존재하지 않는 인스턴스 변수에 접근하는 것)
- 인스턴스 변수 = Non-Static Field
- 클래스 변수 = Static Field
class C1{
static int static_variable = 1;
int instance_variable = 2;
static void static_static(){
System.out.println(static_variable);
}
static void static_instance(){
// 클래스 메소드에서는 인스턴스 변수에 접근 할 수 없음
//System.out.println(instance_variable);
}
void instance_static(){
// 인스턴스 메소드에서는 클래스 변수에 접근 할 수 있음
System.out.println(static_variable);
}
void instance_instance(){
System.out.println(instance_variable);
}
}
생성자
- 객체를 생성할 때 호출
- 값을 반환하지 않는 메소드 (초기화 역할 수행!)
- 반환 타입이 없지만, void를 사용하지 X
- 생성자 이름은 클래스 이름과 동일
- 매개변수가 있는 생성자가 있으면, 기본 생성자를 자동으로 만들어 주지 않음
- 한 생성자에서 다른 생성자를 호출할 때에는 반드시 해당 생성자의 첫 줄에서만 호출할 수 있음
- 접근제어자 이름(매개변수){ }
상속
- 어떤 객체가 있을 때 그 객체의 필드(변수)와 메소드를 다른 객체가 물려 받을 수 있는 기능
- 기존의 객체를 그대로 유지하면서 기능을 추가하는 방법
- 객체지향의 재활용성을 극대화시킨 프로그래밍 기법 (but, 객체 지향을 복잡하게 만들기도,,)
- 코드의 중복 제거로, 유지보수가 편리해짐
- 상속한 클래스를 다시 상속하는 것도 가능! (다중 상속은 불가능,, 모호성 때문!)
- 자식클래스 extends 부모클래스
- super() : 부모 생성자 호출 *super 는 상위 클래스를 가리키는 키워드
* 하위 클래스 생성자에서 super를 사용할 때, super를 제일 먼저 호출 (부모가 초기화되기 전에 자식이 초기화되는 것 방지)
class Calculator {
int num1, num2;
public Calculator(){} // 기본 생성자 (아래 매개변수를 가지는 생성자가 있기 때문에 필요한 경우 추가)
public Calculator(int num1, int num2) {
this.num1 = num1;
this.num2 = num2;
}
public void sum() {
System.out.println(this.num1 + this.num2);
}
public void avg() {
System.out.println((this.num1 + this.num2) / 2);
}
}
class SubstractionableCalculator extends Calculator {
// Calculator class를 상속 (기존 Calculator의 변수와 메소드를 가지고 있음)
public SubstractionableCalculator(int num1, int num2) {
super(num1, num2); // 부모 생성자 호출
}
public void substract() { // 추가 메소드(기능) 정의
System.out.println(this.num1 - this.num2);
}
}
class MultiplicationableCalculator extends SubstractionableCalculator { // 재 상속도 가능
public MultiplicationableCalculator(int num1, int num2) {
super(num1, num2); // 부모 생성자 호출
}
public void multiplication() {
System.out.println(this.num1 * this.num2);
}
}
public class JAVAExample {
public static void main(String[] args) {
// MultiplicationableCalculator는 SubstractionableCalculator클래스를,
// SubstractionableCalculator는 Calculator 클래스를 상속 받았으므로
// sum(), avg(), substract() 메소드 사용이 가능하고, num1, num2 필드를 가짐 !
MultiplicationableCalculator c1 = new MultiplicationableCalculator(10, 20);
c1.sum();
c1.avg();
c1.substract();
c1.multiplication();
}
}
Overriding, Overloading
Overriding
- 하위 클래스에서 메소드를 재정의해서 사용하는 방식
- 기본동작은 폭넓게 적용되고, 예외적인 동작은 더 높은 우선순위를 가짐
- overriding을 하기 위해서는 메소드 이름, 매개변수 숫자/타입/순서, 리턴 타입이 같아야 함
* SubstractionableCalculator 클래스에 sum()을 오버라이딩하면 아래와 같다.
class Calculator {
int num1, num2;
public Calculator(){} // 기본 생성자 (아래 매개변수를 가지는 생성자가 있기 때문에 필요한 경우 추가)
public Calculator(int num1, int num2) {
this.num1 = num1;
this.num2 = num2;
}
public void sum() {
System.out.println(this.num1 + this.num2);
}
public void avg() {
System.out.println((this.num1 + this.num2) / 2);
}
}
class SubstractionableCalculator extends Calculator {
// Calculator class를 상속 (기존 Calculator의 변수와 메소드를 가지고 있음)
public SubstractionableCalculator(int num1, int num2) {
super(num1, num2); // 부모 생성자 호출
}
public void sum() { // 메소드 오버라이딩 !!!!!
System.out.println("실행 결과는 " + (this.num1 + this.num2) + "입니다.");
}
public void substract() { // 추가 메소드(기능) 정의
System.out.println(this.num1 - this.num2);
}
}
class MultiplicationableCalculator extends SubstractionableCalculator { // 재 상속도 가능
public MultiplicationableCalculator(int num1, int num2) {
super(num1, num2); // 부모 생성자 호출
}
public void multiplication() {
System.out.println(this.num1 * this.num2);
}
}
public class JAVAExample {
public static void main(String[] args) {
// MultiplicationableCalculator는 SubstractionableCalculator클래스를,
// SubstractionableCalculator는 Calculator 클래스를 상속 받았으므로
// sum(), avg(), substract() 메소드 사용이 가능하고, num1, num2 필드를 가짐 !
MultiplicationableCalculator c1 = new MultiplicationableCalculator(10, 20);
c1.sum();
c1.avg();
c1.substract();
c1.multiplication();
}
}
Overloading
- 매개변수의 개수나 타입을 다르게 하여, 같은 이름의 메소드를 중복 정의하는 것
- 매개변수가 다르면 이름이 같아도 서로 다른 메소드가 되는 것 !!!
- 상속 관계에서도 오버로딩 사용 가능
- 메소드의 반환값은 메소드 호출 시점에 전달되지 않는 정보이기 때문에 오버로딩 대상이 될 수 없음
public class OverloadingDemo {
void A (){System.out.println("void A()");} // 모두
void A (int arg1){System.out.println("void A (int arg1)");} // 다른
void A (String arg1){System.out.println("void A (String arg1)");} // 메소드 !!
public static void main(String[] args) {
OverloadingDemo od = new OverloadingDemo();
od.A();
od.A(1);
od.A("Hi there !");
}
}
패키지
- 클래스들의 모음집, 클래스명의 고유성을 보장하기 위해 사용
- 패키지를 통해서 라이브러리 간 구분 가능
* 여러 패키지 선언 시, 같은 클래스가 있는 경우 다음과 같이 객체를 생성해야 함
package org.opentutorials.javatutorials.packages.example3;
import org.opentutorials.javatutorials.packages.example1.*; // example1 내부에 class를 사용하고 싶은 경우 import
import org.opentutorials.javatutorials.packages.example2.*;
public class D {
public static void main(String[] args) { // example1 / example3 에 B 클래스 이름이 중복
org.opentutorials.javatutorials.packages.example2.B b = new org.opentutorials.javatutorials.packages.example2.B();
}
}
접근 제어자
: 클래스의 멤버인 변수와 메소드들의 접근 권한을 지정하는 역할
클래스 멤버 정의에 사용하는 접근자
제한자 | 클래스 내부 | 패키지 | 상속받은 클래스 | 이외의 영역 |
private | O | X | X | X |
default | O | O | X | X |
protected | O | O | O | X |
public | O | O | O | O |
클래스 정의에 사용하는 접근자
제한자 | 의미 |
default (지정 x) | 같은 패키지에서만 사용 가능 |
public | 다른 패키지에서도 사용 가능 |
* public 클래스가 포함된 소소코드는 public 클래스의 클래스 명과 소스코드의 파일명이 같아야 함
* 하나의 소스 코드에는 하나의 public 클래스가 존재
추상 메소드 (abstract)
- 메소드의 시그니처만이 정의된 비어있는 메소드
- 구체적인 구현은 하위 클래스에서 오버라이딩 해야 함 (상속을 강제하기 위한 것)
- (추상 클래스는 구체적인 메소드의 내용이 존재하지 않기 때문에 인스턴스화시켜서 사용할 수 없음)
- 추상 메소드를 하나라도 포함하고 있는 클래스는 추상 클래스가 됨
- 공통되는 부분을 상위 클래스에 두어서 코드의 중복, 유지보수의 편의성 등을 꾀할 수 있음
abstract class A{
public abstract int b();
//본체가 있는 메소드는 abstract 키워드를 가질 수 없음
//public abstract int c(){System.out.println("Hello")}
//추상 클래스 내에는 추상 메소드가 아닌 메소드가 존재 할 수 있음
public void d(){
System.out.println("world");
}
}
class B extends A{ // class A 상속
public int b(){return 1;} // 추상 메소드 오버라이드
}
public class AbstractDemo {
public static void main(String[] args) {
B obj = new B();
System.out.println(obj.b());
}
}
final
- 상속/변경을 금지하는 규제
- fianl 필드(변경 불가능), 메소드(오버라이딩 불가능), 클래스(상속 불가능)
인터페이스 (interface)
- 어떤 객체가 특정 인터페이스를 사용한다면 그 객체는 반드시 인터페이스의 메소드들을 구현해야 함
- 하위 클래스에 특정한 메소드가 반드시 존재하도록 강제
- 협업 시 편의성 증가 (규약을 미리 정하기 때문)
- 하나의 클래스가 여러 개의 인터페이스를 구현할 수 있음
- 상속이 가능함
- 인터페이스의 멤버는 반드시 public (구현하여 사용하므로)
- public을 생략하면 접근 제어자 default가 되는 것이 아니라 public이 됨
- interface 인터페이스이름
- 하위클래스 implements 상위클래스
interface I{ // 인터페이스
public void z();
// private void x(); // error. 인터페이스의 맴버는 반드시 public
}
class A implements I{ // 클래스 A는 인터페이스 I를 '구현'
public void z(){} // 없으면 컴파일 에러 발생
}
interface I1{
public void x();
}
interface I2{
public void z();
}
class B implements I1, I2{ // 하나의 클래스가 여러개의 인터페이스를 구현 할 수 있음
public void x(){}
public void z(){}
}
interface I3{
public void x();
}
interface I4 extends I3{ // 인터페이스 상속
public void z();
}
class B implements I4{
public void x(){}
public void z(){}
}
* abstract vs interface
- interface는 class가 아닌 고유한 형태를 가지고 있는 반면, 추상 클래스는 일반적인 클래스 임
- interface는 구체적인 로직이나 상태를 가지고 있을 수 없고, 추상 클래스는 구체적인 로직이나 상태를 가지고 있음
- 추상 클래스의 목적 : 상속 받아서 기능을 이용하고, 확장
- interface의 목적 : 함수의 구현을 강제해서, 구현 객체의 같은 동작을 보장
다형성 (Polymorphism)
- 하나의 메소드나 클래스가 있을 때 이것들이 다양한 방법으로 동작하는 것
- 동일한 조작방법으로 동작시키지만 동작방법은 다른 것 ! ex) ESC(취소), ENTER(실행) / 공통 : 키보드를 누름
- 오버로딩은 다형성의 한 예 !
클래스와 다형성
- 클래스 B를 클래스 A의 데이터 타입으로 인스턴스화 했을 때 클래스 A에 존재하는 맴버만이 클래스 B의 맴버가 됨
- 클래스 B에서 오버라이딩한 맴버의 동작방식은 그대로 유지
= 상속과 오버라이딩 그리고 형변환을 이용한 다형성
class A{
public String x(){return "x";}
}
class B extends A{
public String y(){return "y";}
}
public class PolymorphismDemo1 {
public static void main(String[] args) {
A obj = new B();
obj.x();
// obj.y(); // 실행 불가 (클래스 B는 마치 클래스 A인것처럼 동작)
}
}
class A{
public String x(){return "A.x";}
}
class B extends A{
public String x(){return "B.x";} // 메소드 오버라이딩
public String y(){return "y";}
}
public class PolymorphismDemo1 {
public static void main(String[] args) {
A obj = new B();
System.out.println(obj.x()); // B.x 출력
}
}
인터페이스와 다형성
- 특정 인터페이스를 구현하고 있는 클래스의 데이터 타입으로 인터페이스를 지정 할 수 있음
- 다중 상속이 지원되는 인터페이스의 특징과 결합해서 상속과는 다른 양상의 효과를 만들어 냄
interface I{}
class C implements I{}
public class PolymorphismDemo {
public static void main(String[] args) {
I obj = new C(); // 클래스 C의 데이터 타입으로 인터페이스 I가 될 수 있음
}
}
interface I2{
public String A();
}
interface I3{
public String B();
}
class D implements I2, I3{
public String A(){
return "A";
}
public String B(){
return "B";
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
D obj = new D();
I2 objI2 = new D();
I3 objI3 = new D();
obj.A();
obj.B();
objI2.A();
//objI2.B(); // 오류 발생 (objI2의 데이터 타입이 인터페이스 I2이기 때문)
// objI2의 데이터 타입을 I2로 한다는 것
// = 인스턴스를 외부에서 제어할 수 있는 조작 장치를 인스턴스 I2의 맴버로 제한한다는 의미
//objI3.A(); // 오류 발생
objI3.B();
}
}
예외
try ... catch
- 프로그래밍을 하면 많은 오류 상황에 직면
- 오류를 잘 처리하기 위한 방법들이 필요!
- e.getMessage() : 오류에 대한 기본적인 내용을 출력 (상세 x)
- e.toString() : 조금 더 자세한 정보 제공
- e.printStackTrace() : 리턴 값 x , 메소드가 내부적으로 예외 결과를 화면에 출력 (가장 상세함)
try { // 예외 발생이 예상되는 로직
div = a/b;
} catch(Exception e){ // 예외클래스 인스턴스
// e.getMessage() : 오류의 원인을 사람이 이해하기 쉬운 형태로 리턴
System.out.println("오류 발생 : "+e.getMessage()); // 예외가 발생했을 때 실행되는 로직
}
// 다중 catch 도 가능 !
try {
System.out.println(arr[first] / arr[second]);
} catch(ArrayIndexOutOfBoundsException e){
System.out.println("ArrayIndexOutOfBoundsException");
} catch(ArithmeticException e){
System.out.println("ArithmeticException");
} catch(Exception e){ // ArrayIndexOutOfBoundsException, ArithemeticException 보다 포괄적인 예외 (뒤에 작성해야 함!)
System.out.println("Exception");
}
finally
- 예외 여부와 관계없이 실행되는 로직
- 예외와는 상관없이 반드시 끝내줘야 하는 작업이 있는 경우 사용
try {
System.out.println(arr[first] / arr[second]);
} catch(ArrayIndexOutOfBoundsException e){
System.out.println("ArrayIndexOutOfBoundsException");
} catch(ArithmeticException e){
System.out.println("ArithmeticException");
} catch(Exception e){
System.out.println("Exception");
} finally {
System.out.println("finally");
}
throw와 throws
- 예외 처리를 사용자에게 넘기는 방법
- API 측에서 예외를 던지고 처리할 때 유용
- throw : Exception을 발생시킬 때 사용하는 키워드
- throws : 메소드에서 발생할 수 있는 Exception을 명시적으로 정의할 때 사용
class B{
void run() throws IOException, FileNotFoundException{ // B.run -> C.run
BufferedReader bReader = null;
String input = null;
bReader = new BufferedReader(new FileReader("out.txt"));
input = bReader.readLine();
System.out.println(input);
}
}
class C{
void run() throws IOException, FileNotFoundException{ // C.run -> main
B b = new B();
b.run();
}
}
public class ThrowExceptionDemo {
public static void main(String[] args) {
C c = new C();
try { // 예외처리
c.run();
} catch (FileNotFoundException e) {
System.out.println("out.txt 파일은 설정 파일 입니다. 이 파일이 프로잭트 루트 디렉토리에 존재해야 합니다.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Calculator{
int left, right;
public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void divide(){
if(this.right == 0){
throw new ArithmeticException("0으로 나누는 것은 허용되지 않습니다."); // 예외를 발생시킴
}
try {
System.out.print("계산결과는 ");
System.out.print(this.left/this.right);
System.out.print(" 입니다.");
} catch(Exception e){
System.out.println("\n\ne.getMessage()\n"+e.getMessage());
System.out.println("\n\ne.toString()\n"+e.toString());
System.out.println("\n\ne.printStackTrace()");
e.printStackTrace();
}
}
}
* 기억할만한 주요 Exception
예외 | 사용해야 할 상황 |
IllegalArgumentException | 매개변수가 의도하지 않은 상황을 유발시킬 때 |
IllegalStateException | 메소드를 호출하기 위한 상태가 아닐 때 |
NullPointerException | 매개 변수 값이 null 일 때 |
IndexOutOfBoundsException | 인덱스 매개 변수 값이 범위를 벗어날 때 |
ArithmeticException | 산술적인 연산에 오류가 있을 때 |
* IOException은 예외처리를 강제함
- checked 예외 - RuntimeException을 제외한 Exception의 하위 클래스 (반드시 예외처리 필요)
- unchekced 예외 - RuntimeException의 하위 클래스 (예외처리를 해도되고, 안 해도 됨)
package org.opentutorials.javatutorials.exception;
import java.io.IOException;
class E{
void throwArithmeticException(){
throw new ArithmeticException();
}
void throwIOException1(){
try {
throw new IOException(); // try ... catch가 없는 경우, 에러 발생
} catch (IOException e) {
e.printStackTrace();
}
}
void throwIOException2() throws IOException{
throw new IOException();
}
}
나만의 예외 만들기
- API 쪽에서 예외를 던졌을 때 API 사용자 쪽에서 예외 상황을 복구 할 수 있다면 checked 예외를 사용
- checked 예외를 너무 자주 사용하면 API 사용자를 힘들게 할 수 있기 때문에 적정선을 찾는 것이 중요
- 그냥 프로그램을 종료하는 것이 덜 위험 할 때 unchecked를 사용
class DivideException extends RuntimeException { // 예외 만들기 (unchecked)
DivideException(){
super();
}
DivideException(String message){
super(message);
}
}
class Calculator{
int left, right;
public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void divide(){
if(this.right == 0){
throw new DivideException("0으로 나누는 것은 허용되지 않습니다."); // 예외 던지기
}
System.out.print(this.left/this.right);
}
}
class DivideException extends Exception { // 예외 만들기 (checked)
DivideException(){
super();
}
DivideException(String message){
super(message);
}
}
// 방법 1 (예외처리)
class Calculator{
int left, right;
public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void divide(){
if(this.right == 0){
try { // 예외처리
throw new DivideException("0으로 나누는 것은 허용되지 않습니다.");
} catch (DivideException e) {
e.printStackTrace();
}
}
System.out.print(this.left/this.right);
}
}
// 방법 2 (사용자에게 예외를 던짐)
class Calculator{
int left, right;
public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void divide() throws DivideException{ // 사용자에게 예외를 던짐
if(this.right == 0){
throw new DivideException("0으로 나누는 것은 허용되지 않습니다.");
}
System.out.print(this.left/this.right);
}
}
public class CalculatorDemo {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOprands(10, 0);
try { // 사용자가 예외처리
c1.divide();
} catch (DivideException e) {
e.printStackTrace();
}
}
}
참고