Java 정리 26
18 Dec 2018
Reading time ~5 minutes
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("죄송합니다."); // 사용자에게 에러 메시지를 제공하지 않는다.
}
- 시큐어 코딩가이드 227쪽 에러처리
- 사용자에게 오류 메시지를 노출하지 않는다.
- 운용할때는 printStackTrace는 모두 지운다(문자열처리)
- try ~ catch문에서 catch 예외처리 블럭은 비워두지 않는다.
throws
- 예외날림
- method header 뒤에 정의
- method 내에서 발생한 예외를 메소드를 호출한 곳에서 처리하도록 만드는 것
- method내에서 try~catch를 할 필요가 없다.
- 호출한 곳에서 try~catch로 처리한다.
- 예외가 발생했을 때 처리코드를 업무로직에 대한 코드와 분리할 수 있다.
// 작성법
접근지정자 반환형 메소드명(매개변수,.. ) throws 예외처리클래스명,.. { ... }
// 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() { ...