List 컬렉션은 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지되지 않는다.
순서를 유지하지 않는 데이터 집합으로 데이터 중복을 허용하지 않는다.
Set은 중복을 허용하지 않고 순서도 보장할 필요가 없이 데이터를 보관할 때 사용한다.
구현 클래스 : HashSet, LinkedHashSet, TreeSet 등등
Set은 집합
이다. 집합은 중복되는 원소(요소, 값)를 담을 수 없다.
저장된 값들은 인덱스가 없기 때문에 저장 순서가 고정되어 있지 않다.
값의 중복 제거
와 값의 유무 검사 목적
이 있다. 순서가 부여되면 값들을 .next()를 통해 하나씩 가져올 수 있다. 리턴타입은 Iterator() 이다.
Set은 검색의 목적
이 있기 때문에 순서 정보를 관리할 필요가 없다. 따라서 데이터의 크기에 상관없이 검색에 걸리는 시간이 짧다.
반면 ArrayList는 인덱스를 관리
해야기 때문에 상대적으로 시간이 오래 걸린다. 기능적으로 HashSet과 ArrayList로 구현한 것이 차이가 없다면 HashSet을 이용하는 것이 좋다.
// 객체 생성(Set는 인터페이스이므로 상속한 자식 클래스로 객체를 생성해야 한다.)
HashSet<Double> set = new HashSet<>();
set.add(1.1); // 데이터 추가(무작위 추가됨)
set.remove(1.1); // 해당 데이터 삭제
set.size(); // 컬렉션의 요소(아이템) 개수 확인
set.clear(); // 모든 데이터 삭제(클리어)
set.toString(); // 컬렉션의 값을 문자열로 출력
// HashSet생성
HashSet set1 = new HashSet();
// new에서 타입 파라미터 생략가능
HashSet set2 = new HashSet<>();
// set1의 모든 값을 가진 HashSet생성
HashSet set3 = new HashSet(set1);
// 초기 용량(capacity)지정
HashSet set4 = new HashSet(10);
// 초기 capacity,load factor지정
HashSet set5 = new HashSet(10, 0.7f);
// 초기값 지정
HashSet set6 = new HashSet(Arrays.asList(1,2,3));
기본적으로 빈 HashSet을 생성할 때는 두 번째의 방법을 주로 사용한다.
HashSet도 저장공간보다 값이 추가로 들어오면 List처럼 저장공간을 늘리는데 Set은 한 칸씩 저장공간을 늘리지 않고 저장용량을 약 두배로 늘립니다. 여기서 과부하가 많이 발생합니다. 그렇기에 초기에 저장할 데이터 갯수를 알고 있다면 Set의 초기용량을 지정해주는 것이 좋습니다. List를 쓰나 Set 을 쓰나 동작에 차이가 없다면 HashSet 을 쓰는게 좋습니다. 이게 훨씬 빠릅니다.
set.add(1); //값 추가 set.add(2); set.add(3);
HashSet에 값을 추가하려면 HashSet의 add(value) 메소드를 사용하면 됩니다. 입력되는 값이 HashSet 내부에 존재하지 않는다면 그 값을 HashSet에 추가하고 true를 반환하고 내부에 값이 존재한다면 false를 반환합니다.
set.remove(1); // 값 1 제거 set.clear(); // 모든 값 제거
System.out.println(set.size()); // set 크기 : 3
HashSet은 컬렉션 내에 값이 존재하는지 여부를 확인하는데 최적화된 자료 구조입니다. contains()
메소드를 호출해서 값이 존재하는지 여부를 빠르게 확인할 수 있습니다.
System.out.println(colors.contains("Green"));
System.out.println(set); // 전체출력 [1,2,3] Iterator iter = set.iterator(); // Iterator 사용 while(iter.hasNext()) { // 값이 있으면 true 없으면 false System.out.println(iter.next()); }
Set컬렉션을 그냥 print하게 되면 대괄호 []로 묶여서 set의 전체 값이 출력됩니다. Set에는 인덱스로 객체를 가져오는 get(index) 메소드가 없습니다. 대신 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자(Iterator)
를 제공합니다.
반복자 이터레이터 인터페이스를 구현한 객체를 말하는데 iterator() 메소드를 호출하면 얻을 수 있습니다. Iterator에서 하나의 객체를 가져올 때는 next() 메소드
를 사용합니다. next() 메소드를 사용하기 전에 먼저 가져올 객체가 있는지 확인하는 것이 좋습니다. hasNext() 메소드
는 가져올 객체가 있으면 true를 리턴하고 없으면 false를 리턴합니다.
// set내부에 값 1이 있는지 check : true
System.out.println(set.contains(1));
Iterator<데이터타입> iterator명 = set명.Iterator();
Iterator 안에 담은 set 출력하기
Iterator명.next();
or
while(iterator명.hasNext()) {
iterator명.next(); // 값 없을때까지 계속 출력
}
→ Iterator<String> iterSet = set.iterator();
while(iterSet.hasNext()) {
System.out.print(iterSet.next() + " ");
}
반복문
// Set 컬렉션을 반복하는 방법 1
// 일반적으로 Iterator는 반복자로 사용되므로 반복문에서 사용된다.
// for(초기식; 조건식; 증감식-생략) { }
for (Iterator iterator = set.iterator(); iterator.hasNext() == true;) {
Double value = (Double) iterator.next();
System.out.println(value);
}
// Set 컬렉션을 반복하는 방법 2
// set에 대한 for 문을 더 편리하게 이용하기
// set라는 컬렉션의 요소를 처음부터 끝까지 반복하면서 해당 요소의 아이템을 가져온다.
for (Double item : set) {
System.out.println(item); // 바로 사용 가능
}
iterator는 자바의 컬렉션 프레임 워크에서 컬렉션에 저장되어 있는 요소들을 읽어오는 방법을 표준화 한 것이다. 컬렉션 프레임 워크
란 데이터를 저장하는 클래스들을 표준화
한 설계이다.
반복자는 객체 지향적 프로그래밍에서 배열이나 그와 유사한 자료구조의 내부요소를 순회하는 객체다. 배열 따위의 자료구조 안을 돌면서 들어 있는 데이터에 접근할 수 있게 해주는 객체다. ArrayList
, HashSet
과 같은 컬렉션을 반복하는데 사용할 수 있는 객체다.
자료구조 중 Set의 경우 인덱스가 없기 때문에 일반 for문을 사용할 수 없지만, for-each문은 사용할 수 있다. 그러나 for-each문으로 자료구조를 돌다가 값을 수정, 삭제를 수행하는 코드를 넣으면 @1)ConcurrentModificationException
이 발생한다.
→@1) 반복되는 기본 컬렉션이 Iterator 자체 이외의 다른 항목으로 수정 된 경우에 발생합니다.
List, Map은 데이터 그룹(Collection)
인데 Iterator은 이런 집합체로부터 정보를 얻어낸다고 볼 수 있다.
집합체를 다룰 때는 개별적인 클래스에 대해 데이터를 읽는 방법을 알아야하기 때문에 각 컬렉션에 접근이 힘들어진다. Iterator를 쓰게되면 어떤 컬렉션이라도 동일한 방식으로 접근이 가능하여 그 안에 있는 항목들에 접근할 수 있는 방법을 제공한다.(다형성)
Iterator의 역할은 저장된 데이터들 사이에서 순방향으로 이동하면서 데이터를 가져오거나 삭제할수 있는 기능을 제공합니다.
※ 쉽게 비유하자면, 우리가 책을 보관하는 책장(컬렉션)이 있고, 그곳에는 책(컬랙션 안에 들어갈 객체)이 있다. 이 책장은 도서관 사서(Iterator)가 관리한다. 사서는 무식해서 책장 처음부터 끝까지
한번에 하나씩 밖에 못가져온다.
Java에서 제공하는 컬렉션(Collection)
객체는 보관하고 있는 자료들을 순차적으로 접근하면서 처리할 때 사용하는 Iterator 형식을 제공하고 있다.
Iterator는 반복자
라고 부르며 컬렉션 종류에 관계없이 같은 방법으로 프로그래밍 할 수 있게 해 준다.
Iterator 개체는 컬렉션 개체의 iterator() 메서드
를 호출하여 얻어올 수 있다.
hasNext() 메서드
로 이동이 가능한지 확인한 후에 next() 메서드
로 해당 위치의 보관한 개체를 참조하여 원하는 작업을 수행한다.
이와 같은 Iterator 개체를 사용하면 size 메서드를 얻어와서 반복 처리하는 것보다 속도에서 불리하다.
이는 Iterator 개체를 사용하는 부분이 있기 때문에 불가피한 사항이다.하지만 컬렉션 종류에 관계없이 일관성
있게 프로그래밍할 수 있다는 장점을 갖고 있다.
소스 코드에 어떠한 컬렉션을 사용할 지 정해지지 않았지만 컬렉션 내에 보관한 모든 내용을 출력하는 등의 작업을 먼저 하길 원한다면 Iterator를 사용하는 것은 좋은 선택이다.
→ Iterator가 보통 순방향으로 이동하는데, Iterator가 가리키는 데이터 저장소의 현재 위치에서 이동할 항목이 있는지 체크합니다. 즉, Iterator는 내부적으로 데이터저장소의 자신이 가리키고 있는 지점을 알고 있습니다. 결국, 이동할 항목이 있다면 boolean 타입으로 반환됩니다.
즉, true을 리턴하고 그렇지 않으면 false을 리턴합니다.
→ 실제로 Iterator가 자신이 가리키는 데이터저장소에서 현재위치를 순차적으로 하나 증가해서 이동하는 것을 말합니다. 매개변수 혹은 iterator 되는 타입으로 반환합니다.
즉, 아무 타입으로 반환가능
쉽게 표현하자면...
Iterator<String> iterSet = set.iterator();
while(iterSet.hasNext()) {
System.out.print(iterSet.next() + " ");
}
Iterator iterSet
= set.iterator(); 이 부분은 정확히는 set이라는 객체에서 반복문을 사용하기 위해 첫 진입점을 Iterator라는 클래스의 객체를 얻기위한 코드입니다.
즉, iterSet
이라는 변수에 set.iterator()라는 메소드를 통해 Iterator 객체를 생성한 것이죠.
간단히 말해서 List 컬렉션 객체는 여러개의 객체를 묶은 자료구조라고 했습니다. 이 리스트 구조에서 일단 가장 첫번째 객체의 위치의 메모리 주소를 얻어야 다음 객체를 찾아 갈수 있겠죠? 그 첫번째 객체의 메모리 주소를 얻기 위한 방법이라고 생각하면 됩니다.
그 이후 반복문에서 .next() 통해서 다음 그다음 그다음 주소를 얻어온 것입니다. 그런데 다음 만약 다음 주소가 없다면 즉, 가장 마지막 이라면 다음 주소가 존재하지 않겠죠? 그런데 다음 주소를 부르면 오류가 나서 프로그램이 종료되거나 무한 반복될 가능서이 있죠? 그래서 위와 같이 hasNext() 즉 다음 객체(주소)가 있는지 체크한 후에 있을경우에만 반복하겠다라는 의미 인것이죠
Key
와 Value
라는 것을 한 쌍으로 갖는 자료형이다. Map구조 : key(키)와 Value(값)가 쌍으로 저장되는 형태
.KeySet() : Key들만 모여있는 Set을 리턴
.values() : Value들만 모여있는 Cllection을 리턴
.entrySet() : Key, Value가 하나로 모여있는 Entry들만 모여있는 Set을 리턴
Key Value
Entry fly 날다
Entry walk 걷다
Entry run 뛰다
Set타입
이고 Value는 Collection타입
이다.HashTable
에 저장된다.Maping
한다고 한다.Map 안에서 key/value에 따른 순서 없음
HashMap은 Map 인터페이스를 구현한 대표적인 Map 컬렉션
입니다. Map 인터페이스를 상속하고 있기에 Map의 성질을 그대로 가지고 있습니다. Map은 키와 값으로 구성된 Entry객체를 저장하는 구조를 가지고 있는 자료구조
입니다. 만약 기존에 저장된 키와 동일한 키로 값을 저장하면 기존의 값은 없어지고 새로운 값으로 대치됩니다. HashMap은 이름 그대로 해싱(Hashing)
을 사용하기 때문에 많은 양의 데이터를 검색하는 데 있어서 뛰어난 성능을 보입니다. 내부적으로 같은 값인지 비교할때 equals
나 hashCode
를 사용한다.
HashMap<String, String> map1 = new HashMap<String,String>();
HashMap<String, String> map2 = new HashMap<>();
map.put(1,"사과"); // 값 추가
map.put(2,"바나나");
map.put(3,"포도");
map.remove(1); // key값 1 제거
map.clear(); // 모든 값 제거
System.out.println(map); // 전체 출력 : {1=사과, 2=바나나, 3=포도}
System.out.println(map.get(1)); // key값 1의 value얻기 : 사과
for -map 사용
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class ForEachTest {
public static void main(String[] args) {
int[] arData = { 10, 20, 30, 40, 50};
for(int data : arData) {
System.out.println(data);
}
HashMap<String, Integer> shinee = new HashMap<>();
shinee.put("온유", 34);
shinee.put("종현", 33);
shinee.put("키", 32);
shinee.put("민호", 32);
shinee.put("태민", 30);
System.out.println("=============");
// for-map사용
for(Map.Entry<String, Integer> entry : shinee.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("이름 : " + key + ", 나이 : " +value );
}
}
빠른 for문(향상된 for문, forEach문)
for(자료형 변수명 : 반복자) {
}
단순하게 모든 요소들을 순회할 때 사용하는 것이 좋다.
순서가 중요한 경우(몇번째에 어던 값이 있는지 알 필요가 있는 경우)에는
기존의 for문을 이용한다.
```
// EntrySet() 활용
Set<Entry<Integer, String> entrySet = map1.entrySet();
for (Entry<Integer, String> entry : map.entrySet()) {
System.out.println("[Key]:" + entry.getKey() + " [Value]:" + entry.getValue());
}
// [Key]:1 [Value]:사과
// [Key]:2 [Value]:바나나
// [Key]:3 [Value]:포도
HashMap에 저장된 Key - Value값을 엔트리(키와 값을 결합)의 형태로 Set에 저장하여 반환 Map에 저장되어 있는 Entry객체들을 반환한다. Entry객체는 키(Key)와 값(Value)의 쌍(Pair)을 저장하는 객체로 getKey()와 getValue() 메소드로 각각의 값을 가지고 올 수 있다.
Iterator 사용
// entrySet().iterator()
Iterator<Entry<Integer, String>> entries = map.entrySet().iterator();
while(entries.hasNext()){
Map.Entry<Integer, String> entry = entries.next();
System.out.println("[Key]:" + entry.getKey() + " [Value]:" + entry.getValue());
}
// [Key]:1 [Value]:사과
// [Key]:2 [Value]:바나나
// [Key]:3 [Value]:포도
// keySet().iterator()
Iterator<Integer> keys = map.keySet().iterator();
while(keys.hasNext()){
int key = keys.next();
System.out.println("[Key]:" + key + " [Value]:" + map.get(key));
}
// [Key]:1 [Value]:사과
// [Key]:2 [Value]:바나나
// [Key]:3 [Value]:포도
key 값에 따라 오름차순 정렬, 정렬 때문에 대량의 데이터 삽입/ 삭제시 오래걸림 TreeSet 클래스는 데이터가 정렬된 상태로 저장되는 이진 검색 트리(binary search tree)
의 형태로 요소를 저장합니다. 이진 검색 트리는 데이터를 추가하거나 제거하는 등의 기본 동작 시간이 매우 빠릅니다. JDK 1.2부터 제공되는 TreeSet 클래스는 NavigableSet 인터페이스
를 기존의 이진 검색 트리의 성능을 향상시킨 레드-블랙 트리(Red-Black tree)
로 구현합니다.
TreeSet 클래스는 Set 인터페이스를 구현하므로, 요소를 순서에 상관없이 저장하고 중복된 값은 저장하지 않습니다.
TreeSet ts = new TreeSet();
// add() 메소드를 이용한 요소의 저장
ts.add(30);
ts.add(40);
ts.add(20);
ts.add(10);
// Enhanced for 문과 get() 메소드를 이용한 요소의 출력
for (int e : ts) {
System.out.print(e + " ");
}
// remove() 메소드를 이용한 요소의 제거
ts.remove(40);
// iterator() 메소드를 이용한 요소의 출력
Iterator<Integer> iter = ts.iterator();
while (iter.hasNext()) {
System.out.print(iter.next() + " ");
}
// size() 메소드를 이용한 요소의 총 개수
System.out.println("이진 검색 트리의 크기 : " + ts.size()); // 이진 검색 트리의 크기 : 3
// subSet() 메소드를 이용한 부분 집합의 출력
① System.out.println(ts.subSet(10, 20)); // [10]
② System.out.println(ts.subSet(10, true, 20, true)); // [10, 20]
위의 예제처럼 TreeSet 인스턴스에 저장되는 요소들은 모두 정렬된 상태로 저장된다.
사용 방법 및 메소드 정리
// 객체 생성(Set는 인터페이스이므로 상속한 자식 클래스로 객체를 생성해야 한다.)
//Map<String, String> map = new HashMap<>();
map.put("1", "Hello"); // 데이터 추가(무작위 추가됨)
map.get(key) // Map 안의 값 가져오기
map.remove("1"); // 해당 데이터 삭제
map.size(); // 컬렉션의 요소(아이템) 개수 확인
map.clear(); // 모든 데이터 삭제(클리어)
map.toString(); // 컬렉션의 값을 문자열로 출력
map.replace(key, value) // Map 안의 내용 변경하기
map.containsKey(key)
map.containsValue(value) // Map 안에 특정 key, value 들어있는지 확인
map.isEmpty() // Map의 크기가 0인지 확인
map.getOrDefault(key, default); // Map에 key에 해당하는 값이 없을 경우 default 호출
반복문(map.keyset()와 keyset.iterator()을 한줄로)
for (Iterator iterator = map.keySet().iterator(); iterator.hasNext();) {
String string = (String) iterator.next();
System.out.println(string);
}
keySet()
keySet은 맵에서 key, value 한 쌍을 이루는 요수들 중에 key 들만 배열로 가져오는 메소드입니다.
System.out.println(map.keySet());
출력해보면 아래와 같이 키들만 배열로 가지고 있죠. 이것을 통해 각 키에 해당하는 값을 가져오죠
즉, 키에 해당되는 값을 가져오기 위해 키들에 대한 배열을 얻어온 것입니다.
[no2, no1, no3]
entrySet()
entrySet은 맵에서 key, value 한 쌍에서 값을 가져오기 위해
즉, 최종적으로 key 해당되는 value를 가져오기 위해 위와 같이 key를 얻고 다시 값을 얻고 하는 번거로운(?) 작업을 좀더 편하게 한번에 가져오기 위한 발전된 방법이라고 보면 됩니다.
즉, for 문을 사용하기위해 위와 같이 2번(?)에 걸쳐 가져와야 할 것을 한번에 그냥 key, value를 얻어오기 위한 발전된 formap 이라고 보시면됩니다. 따라서 for문 이름에서도 보듯 formap 이라고 명명합니다.
System.out.println(map.entrySet());
[no2=안녕하세요, no1=HelloWolcome, no3=HelloWolcome]
이와 같이 런타임 오류를 미리 감지하여 발생될 가능성이 있는 오류들을
사전에 예외처리 하는 코딩 기술을 말한다.
컴파일이 되기 전에 문법적 또는 코드 작성 오류이다.
컴파일 때 모두 알수 있으며, 이클립스 사용시 작성중에 발견할 수 있다.
컴파일중 발견되지 않는 오류로 실행중에 발생되는 오류를 말한다.
실행중에 발견되는 오류이므로 수많은 테스트를 통해 미리 잡아내야 한다.
Exception(일반예외)
모든 예외 클래스의 부모 클래스로서 일반적으로 Exception으로 적으면
모든 예외 클래스들이 Exception으로 업캐스팅 된다.
RuntimeException(실행예외)
이 두가지를 처리하기 위해서 자바에서는 java.lang.Exception이라는 최상위 부모 클래스를 제공
try catch : 예외상황 시도시 catch로 잡아낸다.
throw : 예외상황을 당장 처리하지 않고 상위단으로 던져버린다.
→ 범위 잡고 Alt + Shift + z : 알아서 try - catch문이 작성된다
try catch 기본구조
try {
...
} catch(예외1) {
...
} catch(예외2) {
...
...
}
try 문안의 수행할 문장들에서 예외가 발생하지 않는다면 catch문 다음의 문장들은 수행이 되지 않는다. 하지만 try 문안의 문장을 수행하는 도중에 예외가 발생하면 예외에 해당되는 catch문이 수행된다. 보통 개발하다보면 어디서 예외가 나올지 모르기 때문에 전부 try문에 넣어 놓고 돌린다.
// 숫자를 0으로 나누었을 때 발생하는 예외를 처리하려면 다음과 같이 할 수 있다.
int c;
try {
c = 4 / 0;
} catch(ArithmeticException e) {
c = -1; // 예외가 발생하여 이 문장이 수행된다.
}
ArithmeticException이 발생하면 c에 -1을 대입하도록 예외를 처리한 것이다.
ArithmeticException e에서 e는 ArithmeticException 클래스의 객체, 즉 오류 객체에 해당한다.
이 오류 객체를 통해 해당 예외 클래스의 메서드를 호출할수 있다.
finally
프로그램 수행 도중 예외가 발생하면 프로그램이 중지되거나 예외 처리에 의해 catch 구문이 실행된 다. 하지만 어떤 예외가 발생하더라도 반드시 실행되어야 하는 부분이 있어야 한다면 어떻게 해야 할까?
public class Sample {
public void shouldBeRun() {
System.out.println("ok thanks.");
}
public static void main(String[] args) {
Sample sample = new Sample();
int c;
try {
c = 4 / 0;
sample.shouldBeRun(); // 이 코드는 실행되지 않는다.
} catch (ArithmeticException e) {
c = -1;
}
}
}
위 예를 보면 sample.shouldBeRun() 메소드는 절대로 실행될 수 없을 것이다. 왜냐하면 4/0에 의해 ArithmeticException이 발생하여 catch구문으로 넘어가기 때문이다.
shouldBeRun() 메소드는 반드시 실행되어야 하는 메소드라고 가정해 보자. 이런 경우를 처리하기 위해 자바는 finally 구문을 제공한다.
public class Sample {
public void shouldBeRun() {
System.out.println("ok thanks.");
}
public static void main(String[] args) {
Sample sample = new Sample();
int c;
try {
c = 4 / 0;
sample.shouldBeRun(); // 이 코드는 실행되지 않는다.
} catch (ArithmeticException e) {
c = -1;
} finally {
sample.shouldBeRun(); // 예외에 상관없이 무조건 수행된다.
}
}
finally 구문은 try 문장 수행 중 예외발생 여부에 상관없이 무조건 실행된다. 따라서 위 코드를 실행하면 sample.shouldBeRun() 메소드가 수행되어 "ok, thanks" 문장이 출력될 것이다.
Exeption 발생!
Try 블록 수행 -> Catch 블록 수행 -> Finally 블록 수행 (생략가능)
Exeption 미발생!
Try 블록 수행 -> Finally 블록 수행 (생략가능)
import java.io.IOException;
public class ExceptionExam {
public static void main(String[] args) {
// 오류로 시스템이 종료하는 상황
// System.out.println("1. 시스템 시작");
// int score = 100;
// int avg = score/0;
// System.out.println(avg);
// System.out.println("2. 시스템 종료");
// 시스템이 종료하지 않도록 try catch를 사용해 보자
System.out.println("1. 시스템 시작");
try {
// 여기에 우리가 사용하는 일반적인 코드를 작성한다.
int score = 100;
int avg = score/0;
System.out.println(avg);
} catch (Exception e) {
// 예외상황(오류) 발생시 작성할 코드를 작성한다.
System.err.println("3. 오류가 발생했습니다.");
// e.printStackTrace();
}
System.out.println("2. 시스템 종료");
try {
print(0);
} catch (Exception e) {
// e.printStackTrace();
System.err.println("5. Throws 오류 입니다.");
}
System.out.println("4. 프로그램 종료");
}
public static int print(int a) throws Exception {
int score = 100;
int avg = score/a;
return avg;
}
}
import java.util.InputMismatchException;
import java.util.Scanner;
public class ExceptionTest {
public static void main(String[] args) {
try {
System.out.println(Integer.parseInt("10"));
System.out.println(Integer.parseInt("Hello"));
System.out.println(Integer.parseInt("30"));
} catch (NumberFormatException e) {
System.out.println("숫자로 이루어진 문자열만 바꿀 수 있습니다.");
} finally {
System.out.println("꼭 해야하는 문장");
}
System.out.println("===============================================");
// 두 수 입력받아서 앞으 수를 뒤의 수로 나눈 결과 출력하기
// 입력 : 정수 두개 입력받기
Scanner sc = new Scanner(System.in);
while(true) {
sc = new Scanner(System.in);
// 어디서 오류가 발생할지 모르니 전부 try이로 잡아둔다.
try {
System.out.print("첫번째 정수 : ");
int num1 = sc.nextInt();
System.out.print("두번째 정수 : ");
int num2 = sc.nextInt();
// 처리 : 위에서 입력받은 두 수 가져와서 앞의 수를 뒤의 수로 나누기
int result = num1 / num2;
// 출력 : 위에서 처리된 결과값 출력하기
System.out.println("결과 : " + result);
// ③ 2번에서 확인한 오류로 catch문 만들기
} catch(ArithmeticException ae) {
System.out.println("0으로는 나눌 수 없습니다.");
} catch(InputMismatchException ims) {
System.out.println("숫자만 입력하세요.");
}
catch (Exception e) { // Exception은 모든 예외클래스의 부모 클래스로서 어떤 예외코드가 발생해도 Exception으로 업캐스팅된다.
// 이것은 사용자를 위한 편의성을 위해 적어놓은것
// ① 여기서 오류가 있다고 연락이 오면 주석처리
// ④ 주석 해제
System.out.println("알 수 없는 오류 발생 / 개발자에게 연락하세요~");
// ② 무슨 오류인지 확인
// ⑤ 주석처리
// System.out.println(e); // 출력 : java.lang.ArithmeticException: / by zero ← 이거보고 확인
}
}
}
}
실행예외 : NullPointerException
(NullPointerException : 실제 참조할 대상이 null인데 참조하려고 할때 발생하는 예외)
일반예외 :
ClassNotFoundException: 해당 클래스가 존재하지 않으면 발생하는 일반 예외)
forName 메서드 : 파라미터로 클래스 정보를 넘겨주고, 해당 클래스가 존재하면 갹체를 리턴해주는 메서드
// 존재하지 않는 파일을 열려고 시도
BufferedReader br = new BufferedReader(new FileReader("나없는파일"));
br.readLine();
br.close();
// 다음과 같은 오류가 발생한다.
// 존재하지 않는 파일을 열려고 시도하면 FileNotFoundException라는 이름의 예외가 발생한다.
Exception in thread "main" java.io.FileNotFoundException: 나없는파일 (지정된 파일을 찾을 수 없습 니다)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileReader.<init>(Unknown Source)
...
// 이번에는 0으로 다른 숫자를 나누는 경우
int c = 4 / 0;
// 위 코드가 실행되면 다음과 같은 오류가 발생
// 4를 0으로 나누면 ArithmeticException 예외가 발생한다.
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:14)
// 마지막으로 한가지 오류만 더 들어보면 다음의 오류는 빈번히 일어난다.
int[] a = {1, 2, 3};
System.out.println(a[3]);
// 이렇게 오류가 발생
// a[3]은 a 배열의 4번째 값이므로 a 배열에서 구할 수 없는 값이다. 그래서 ArrayIndexOutOfBoundsException 오류가 발생했다.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at Test.main(Test.java:17)
ArithmeticException : 정수를 0으로 나눌경우 발생
ArrayIndexOutOfBoundsExcetion : 배열의 범위를 벗어난 index를 접근할 시 발생
ClassCastExcetion : 변환할 수 없는 타입으로 객체를 반환 시 발생
NullPointException : 존재하지 않는 레퍼런스를 참조할때 발생
IllegalArgumentException : 잘못된 인자를 전달 할 때 발생
IOException : 입출력 동작 실패 또는 인터럽트 시 발생
OutOfMemoryException : 메모리가 부족한 경우 발생
NumberFormatException : 문자열이 나타내는 숫자와 일치하지 않는 타입의 숫자로 변환시 발생
※ 빈도수가 높은 예외
NullPointException
자바 프로그램에서 가장 빈번하게 발생하는 에러입니다. 이 에러는 객체 참조가 없는 상태, 즉 null 값을 갖는 참조변수로 객체 접근 연사자인 토드(.)를 사용했을 때 발생합니다. 객체가 없는 상태에서 객체를 사용하려 했으니 예외가 발생하는 것입니다.
ArrayIndexOutOfBoundsExcetion
배열에서 인덱스 범위를 초과하여 사용할 경우 발생합니다. 예를 들어 길이가 3인 int[]arr = new int[3] 배열을 선언했다면 0 ~ 2까지의 index만 사용할 수 있습니다. 하지만 이 배열의 index가 -1 이나 3을 참조하는 순간 예외가 발생합니다.
NumberFormatException
개발을 하다보면 문자열로 되어있는 데이터를 숫자타입으로 변경하는 경우가 자주발생하는데 숫자타입으로 변경할 수 없는 문자를 치환시키려고 하면 발생하는 에러입니다.
ClassCastExcetion
타입 변환은 상위 클래스와 하위클래스간에 발생하고 구현 클래스와 인터페이스간에도 발생합니다. 이런 관계가 아니면 클래스는 다른 클래스로 타입을 변환할 수 없습니다. 하지만 이 규칙을 무시하고 억지로 타입을 변환시킬경우 발생하는 에러입니다.
try{ //에러가 발생할 수 있는 코드
throw new Exception(); //강제 에러 출력
}catch (Exception e){ //에러시 수행
e.printStackTrace(); //오류 출력(방법은 여러가지)
throw e; //최상위 클래스가 아니라면 무조건 던져주자
}finally{ //무조건 수행
}
throw :
에러를 고의로 발생시킬 때 사용합니다.
최상단 클래스를 제외한 나머지 클래스에서의 예외처리는 반드시 Throw를 해주어야 합니다.
그렇지 않으면 예외처리를 해주었음에도 불구하고 Main에서는 Exception을 전달받지 못하여
개발자가 예외를 인지못하는 경우가 생깁니다.
throws :
자신을 호출한 상위 메소드로 에러를 던지는 역할을 합니다.
Throws는 메소드에서 잠재적으로 어떤 Exception이 발생할 수 있는지 명시하는 키워드입니다.
public class File {
public File(String pathname) {
if (pathname == null) {
throw new NullPointerException();
}
this.path = fs.normalize(pathname);
this.prefixLength = fs.prefixLength(this.path);
}
}
import java.util.Scanner;
public class Try_Catch2 {
public static void main(String[] args) {
// 사용자정의 예외처리
// 예외가 발생할 경우가 아님에도 불구하고 조건에 해당하면 강제로 예외처리
// throws : 호출한 영역으로 예외처리를 던짐
// throw : 강제로 예외를 발생시킨다.
//
try {
scoreInput();
} catch (Exception e) {
System.out.println("오류메세지 : " + e.getMessage());
}
System.out.println("정상종료");
}
// throws Exception가 main으로 scoreInput 오류를 던져서 거기서 처리하게 만듬
// 즉, 해당 메서드가 실행되는 곳에서 오류를 잡아내는 겁니다.
// 에러가 일어난 곳에서 잡아내는것이 아니라
public static void scoreInput() throws Exception {
// 점수가 0~100점 아니라면 강제로 익셉션 발생하게
Scanner sc = new Scanner(System.in);
int score = 0;
System.out.print("점수를 입력하세요 : ");
score = sc.nextInt();
if(score < 0|| score > 100) {
throw new Exception("점수가 잘못 입력되었습니다.");
} else {
System.out.println("나의 점수는 " + score + "입니다.");
}
}
}
File 클래스의 생성자를 보면 위와 같이 경로가 null 일 때
throw를 통해서 강제로 에러를 발생하는 것을 볼 수 있습니다.
위와 같이 throw를 통해서 에러를 강제로 만들었을 때
내부적으로 try/catch로 해결하기 않으면 throws를 사용하여
호출한 곳으로 에러를 던지게 됩니다.
public class Try_Catch {
public static void main(String[] args) {
int x, sum;
try {
x = Integer.parseInt(args[0]);
sum =x/0;
} catch (ArithmeticException e) { // 첫번째 예외처리
System.out.println("0으로 나누어서 예외발생");
} catch (Exception e) { // 두번째 예외처리
System.out.println("arg[0]를 주지 않아 예외발생");
}
// 자신이 처리해야 하는 예외들을 찾아보시고 알맞게 처리해 주는 게 필요합니다.
// 하지만 주의하실 점은 catch 구문도 순서가 있기에, 맨 처음부터 exception(모든 예외)로
// 선언해 준다면, 두 번째, 세 번째... 선언한 catch 문은 수행할 수가 없습니다.
System.out.println("================================");
String numStr = "11a";
int sum2 = 0;
int num = 0;
try {
// Integer.parseInt()에서 NumberFormatException이 발생하면
// 그 아래에 있는 코드들이 수행되지 않고 catch에 있는 코드가 실행됩니다.
num = Integer.parseInt(numStr);
sum2 = sum2 + num;
} catch (NumberFormatException e) {
// catch exception
num =0;
sum2 = 0;
}
// 위의 예제는 NumberFormatException가 발생했을 때만 catch 코드가 수행되며 예외를 처리합니다.
// 만약 RuntimeException이 발생하면 예외를 처리하지 못하여 프로그램이 죽습니다.
// 만약 다음과 같이 catch (Exception e)으로 Exception에 대해서
// 예외를 처리하면 거의 모든 예외가 처리될 수 있습니다.
}
}
package put_in;
class A {
int a;
static int score = 100;
public int print(int a) throws Exception {
int avg;
avg = score/a;
return avg;
}
}
public class Try_Catch {
public static void main(String[] args) {
System.out.println("1. 시스템 시작");
try {
int score = 100;
int avg = score/0;
System.out.println(avg);
} catch (Exception e) {
System.err.println("3. 오류가 발생했습니다.");
}
System.out.println("2. 시스템 종료");
try {
A a = new A();
a.print(0);
} catch (Exception e) {
System.err.println("5. throws 오류 입니다.");
}
System.out.println("4. 프로그램 종료");
}
}