Java 정리 26 - Compile Exception, throws, 객체 복제, instanceof, throw, 사용자 정의 예외처리
Compile Exception
byte code를 정상적으로 생성하기 위한 것.
개발자가 반드시 처리해야하는 예외.
public class UseCompileException { public static void main(String[] args) { try { Object obj = Class.forName("java.lang.String1"); System.out.println("로딩한 클래스 "+obj); } catch (ClassNotFoundException cnfe) { System.err.println("죄송합니다."); System.err.println("예외 메시지 출력 : "+cnfe.getMessage()); System.err.println("예외처리 객체와 메시지 : "+cnfe); // printStackTrace는 정보를 stack에 담고 출력하여 // 시간이 조금 더 걸림(때문에 출력결과가 다른 out메시지가 먼저 출력가능) cnfe.printStackTrace(); System.out.println("---"); // out.println은 printStackTrace보다 먼저 출력 // 순서대로 출력을 원할땐 err.println로 출력하면 된다 } }}
new없이 객체화
외부에서 클래스 읽어들여서 바깥에서 객체화를 할 수 있다.
new 없이 객체화가 가능
외부클래스 로딩할때 쓰는 메소드가 Class클래스의 forName 메소드
클래스가 없으면 ClassNotFoundException 발생
try { Object obj = Class.forName("java.lang.String"); System.out.println("로딩한 클래스 "+obj);} catch (ClassNotFoundException cnfe) { System.out.println("죄송합니다."); // 사용자에게 에러 메시지를 제공하지 않는다.}
// method내에서 발생한 예외를 method를 호출한 곳에서 처리하는 throws의 사용.public class UseThrows { public void test() throws ClassNotFoundException { // throws로 예외를 날리면 method안에서 try~catch를 할 필요가 없다. String className = "date181217.UseRuntimeException"; if (new Random().nextBoolean()) { className = "java.long.Integer"; } Object obj = Class.forName(className); System.out.println("클래스 로딩 "+obj); } public static void main(String[] args) { UseThrows ut = new UseThrows(); try { // method를 호출하여 일 수행하다보면 문제가 발생할 수도 있다. ut.test(); } catch (ClassNotFoundException cnfe) { System.err.println("죄송합니다."); cnfe.printStackTrace(); System.out.println("정상출력"); } }}
생성자에도 throws 사용가능
public class ConstructorThrows { public ConstructorThrows() throws ClassNotFoundException { Class.forName("java.long.Object"); System.out.println("객체생성"); } public static void main(String[] args) { try { ConstructorThrows ct = new ConstructorThrows(); System.out.println("객체화 성공 : "+ct); } catch (ClassNotFoundException cnfe) { System.err.println("객체화 중 예외발생"); cnfe.printStackTrace(); } }}
public class ConstructorThrowsSub extends ConstructorThrows { // 부모클래스의 생성자가 컴파일 예외를 날리면 자식클래스 // 생성자에서도 예외를 날려야 한다. public ConstructorThrowsSub() throws ClassNotFoundException { super(); // super는 생성자의 첫번째 줄에서만 기술할 수 있음 // 부모클래스의 생성자가 Compile Exception을 throws로 날리면 // 생성자를 호출하는 자식생성자에서는 반드시 처리해야하는 // 문법상 에러가 발생하게 된다. /*try { super(); // error! } catch (ClassNotFoundException cnfe) { }*/ } public static void main(String[] args) { }}
개체 복제
모든 객체는 복제될 수 있는 method를 가지고 있다. (Object 클래스의 clone())
변화된 상태를 저장해야하는 경우 객체를 복제
Stack, history기능을 구현할때 사용
call by reference로 그냥 복제안하고 한 객체를 여러번 push한다 한들 stack에서 pop하면 한개의 객체만 나오게 된다.
복제해서 push하고 나중에 pop을 한다면 상태변화가 저장된 객체들을 사용가능.
Cloneable interface 구현한 객체만 복제된다.
인스턴스변수가 없는 객체는 복제할 필요가 없다.
MyClass mc = new MyClass();...MyClass o1 = (MyClass)mc.clone(); // mc를 사용하다 현재 상태를 저장하고 싶을때 clone// clone의 반환형은 Object, 부모는 자식을 모르기떄문에 형변환// o1은 복제된 객체(저장된 시점에 mc의 상태를 저장한 객체)// 복제가 안 될 수도 있을땐 try~catch로 예외처리try { MyClass o2 = (MyClass)mc.clone();} catch (CloneNotSupportedException cnse) {}
public class ObjectClone implements Cloneable { private int i; public static void main(String[] args) { ObjectClone oc = new ObjectClone(); oc.i = 100; try { // 현재 상태를 저장하기 위해 복제 ObjectClone oc1 = (ObjectClone)oc.clone(); System.out.println("복제성공 oc : "+oc+", oc1 : "+oc1); } catch (CloneNotSupportedException cnse) { System.err.println("복제실패"); cnse.printStackTrace(); } }}
히스토리 처리
clone은 접근지정자가 protected이기 때문에 해당 클래스안에서 복제한걸 반환해줘야 한다.
public class Data implements Cloneable { private String school; public Data() { super(); } public Data(String school) { this.school = school; } public String getSchool() { return school; } public void setSchool(String school) { this.school = school; } public Data getData() throws CloneNotSupportedException { // clone()은 protected 접근지정자를 가지고 있어 // 외부 클래스에서 다른 클래스의 clone()를 호출할 수 없다. Data data = (Data)this.clone(); return data; }}
public class DataClone { private Stack<Data> historyStack; public DataClone() { historyStack = new Stack<Data>(); } public void useData() throws CloneNotSupportedException { Data d = new Data("쌍용유치원"); d.setSchool("쌍용초등학교"); Data d1 = d.getData(); historyStack.push(d1); d.setSchool("쌍용중학교"); Data d2 = d.getData(); historyStack.push(d2); d.setSchool("쌍용고등학교"); Data d3 = d.getData(); historyStack.push(d3); d.setSchool("쌍용대학교"); Data d4 = d.getData(); historyStack.push(d4); getHistoryData(); } public void getHistoryData() { Data temp = null; while(!historyStack.isEmpty()) { temp = historyStack.pop(); System.out.println(temp); // temp는 복제된 객체들, 주소가 다 다르다 System.out.println(temp.getSchool()); } } public static void main(String[] args) { try { new DataClone().useData(); } catch (CloneNotSupportedException e) { System.err.println("복제되지 않습니다."); e.printStackTrace(); } }}
instanceof
문자열로된 연산자
생성된 객체가 특정 클래스로 부터 나왔는지 비교할 때 사용.
객체명 instanceof 클래스|인터페이스명; // boolean 반환
public interface Test { public String getData();}public class TestImpl implements Test { @Override public String getData() { return "2018-12-18"; }}public class TestImpl1 implements Test { @Override public String getData() { SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy EEEE"); return sdf.format(new Date()); }}public class UseTest { public void useTest(Test test) { if (test instanceof TestImpl) { String date = test.getDate(); System.out.println(date); } else { System.out.println("TestImpl만 처리합니다."); } } public static void main(String[] args) { UseTest ut = new UseTest(); TestImpl ti = new TestImpl(); TestImpl1 ti1 = new TestImpl1(); ut.useTest(ti); ut.useTest(ti1); }}
throw
예외 강제 발생
특정 조건에 일치하면 예외를 강제로 발생시켜 처리하는 것
강제 발생된 예외는 반드시 try~catch로 처리하든 throws로 예외를 날리든 처리해야 한다.
사용자정의 예외처리클래스와 주로 사용
throw new 예외처리클래스();
사용자 정의 예외처리
업무에 맞는 예외처리 클래스가 없다면 그 예외처리 클래스를 만드는 것.
Exception을 상속받거나 Runtime Exception을 상속받아서 만든다.
// 사용자 정의 예외처리 클래스// 1. Exception이나 RuntimeException을 상속받는다.@SuppressWarnings("serial")public class TobaccoException extends Exception { public TobaccoException() { this("담배예외 - 폐암의 원인 흡연! 그래도 피우시겠습니까?"); } public TobaccoException(String msg) { super(msg); }}
// 예외를 강제로 발생public class TestThrow { // 길을 가다가 담배를 피우는 학생을 보면 정의사회를 구현한다. public void teacksung() throws TobaccoException { String[] grade = { "초등학생", "중학생", "고등학생" }; String randomGd = grade[new Random().nextInt(grade.length)]; if (randomGd.equals("초등학생")) { throw new TobaccoException(); } else { System.out.println(randomGd+"이 담배를 피우면 모른척 지나간다. ('' )( '')"); } } public static void main(String[] args) { TestThrow tt = new TestThrow(); try { tt.teacksung(); } catch (TobaccoException te) { te.printStackTrace(); } }}
RuntimeException을 상속받아 사용자 정의 예외클래스를 만들면 try~catch 안해도 된다.
일반 Exception(Compile Exception)은 반드시 예외처리를 해야 함
public class TobaccoException extends RuntimeException {...// RuntimeException의 경우 try~catch나 throws를 안해도 됨public void teacksung() { ...