[프로그래밍 기초] Java문법 3일차
Java문법 종합반 4, 5 주차
예외처리
컴파일 에러 - 실행 자체가 안된다. 문법상으로 틀렸든 오타가 있든 수정을 해서 해결해야 된다.
런타임 에러 - 프로그램이 작동 중에 나오는 에러로 예외처리를 통해 대처한다.
throws를 통해 여러 개의 예외처리도 가능하다.
예외처리를 처음부터 확인하기 어려운데 실행하면서 에러가 발생하면 에러 코드를 보고 해당 에러가 왜 일어났는지와 에러에 대한 예외처리 클래스도 확인할 수 있다.
// 예외처리 클래스를 생성
public class OurBadException extends Exception { // 상위 예외처리에서 상속받는다.
public OurBadException () { // 생성자
super("위험한 행동을 하면 예외처리를 꼭 해야함");
}
}
// 클래스를 통해서 체크를 한다.
public class OurClass {
private final boolean just = true;
public void thisMethodIsDangerous () throws OurBadException { // 만든 예외 처리 지정
if (just) {
throw new OurBadException(); // 확인을 위해 강제로 예외처리를 던저준다.
}
}
}
// try catch문을 통한 예외처리
public class StudyException {
public static void main(String[] args) {
OurClass ourClass = new OurClass();
// 즉 try 블럭 내의 구문을 실행하다가 예외가 발생하면
// 예외가 발생한 줄에서 바로 코드 실행을 멈추고
// 여기 있는 catch 블럭 내의 코드가 실행됩니다.
try {
// 1. 위험한 요소가 있는 부분을 try문에 넣어서 실행
ourClass.thisMethodIsDangerous(); // 에러 불러오기
System.out.println("트라이 끝"); // 위에서 에러가 나므로 실행안됨.
} catch (OurBadException e) { // 해당 발생에러
// 2. 예외가 발생하면, catch문에서 대처를 한다.
System.out.println(e.getMessage()); // 에러에 대한 메세지 출력 "위험한 행동을 하면 예외처리를 꼭 해야함"
System.out.println("캐치 끝");
} finally {
// 3. 예외의 발생 여부와 상관없이, 실행시켜야 하는 코드가 들어갑니다.
// 무조건 실행되는 코드가 들어가는 블럭입니다.
System.out.println("우리는 방금 예외를 handling 했습니다!");
}
System.out.println("문장 끝");
}
}
// 예외처리 회피
public class StudyException {
public static void main(String[] args) throws OurBadException { // throws를 이용해 회피 가능
OurClass ourClass = new OurClass();
ourClass.thisMethodIsDangerous();
}
}
제네릭(Generic)
데이터 타입을 미리 임시로 지정을 해두고 사용하고 싶은 타입을 넣어서 사용해서 활용에 있어 자유롭다.
클래스와 메서드에만 사용이 가능하다.
멤버변수에 static을 사용할 수 없다.
타입 변수는 래퍼클래스 및 클래스만 올 수 있다.
와일드카드를 이용해 이름에 제한을 두지 않고 표현이 가능하다.
// 기본적 형태
public class Generic<T> {
private T t;
public T get() {return this.t;}
public void set(T t) {this.t = t;}
Generic<String> stringGeneric = new Generic<>();
// 다수의 타입변수를 사용 할 수 있다.
public class Generic<T, U, E> {
public E multiTypeMethod(T t, U u) { ... }
}
Generic<Long, Integer, String> instance = new Generic();
instance.multiTypeMethod(longVal, intVal);
// 제네릭의 활용
class LandAnimal { public void crying() { System.out.println("육지동물"); } }
class Cat extends LandAnimal { public void crying() { System.out.println("냐옹냐옹"); } }
class Dog extends LandAnimal { public void crying() { System.out.println("멍멍"); } }
class Sparrow { public void crying() { System.out.println("짹짹"); } }
class AnimalList<T extends LandAnimal> { // extends 키워드를 이용해 특정 타입만 사용하도록 제한 가능.
ArrayList<T> al = new ArrayList<T>();
void add(T animal) { al.add(animal); }
T get(int index) { return al.get(index); }
boolean remove(T animal) { return al.remove(animal); }
int size() { return al.size(); }
}
public class Generic01 {
public static void main(String[] args) {
AnimalList<LandAnimal> landAnimal = new AnimalList<>(); // Java SE 7부터 생략가능함.
landAnimal.add(new LandAnimal());
landAnimal.add(new Cat());
landAnimal.add(new Dog());
// landAnimal.add(new Sparrow()); // 오류가 발생함.
for (int i = 0; i < landAnimal.size() ; i++) {
landAnimal.get(i).crying();
}
}
}
// 와일드카드
<?> // 타입 변수에 모든 타입을 사용할 수 있음.
<? extends T> // T 타입과 T 타입을 상속받는 자손 클래스 타입만을 사용할 수 있음.
<? super T> // T 타입과 T 타입이 상속받은 조상 클래스 타입만을 사용할 수 있음.
프로세스와 스레드
프로세스(process)란 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 것
스레드(thread)란 프로세스 내에서 실제로 작업을 수행하는 주체
스레드를 생성하는 방법
- Runnable 인터페이스를 구현, Thread 클래스를 상속, main에 람다식으로 바로 적용
- run() 메서드를 작성해 작업을 추가
멀티 스레드(multi thread) - 둘 이상의 스레드가 동시에 작업을 수행하는 것
데몬 스레드(deamon thread) - 보조적인 역할을 하는 스레드로 일반 스레드가 모두 종료되면 데몬 스레드의 역할이 남아 있어도 자동으로 종료된다.
다른 여러 기능들은 한번 다시 확인해 보면서 정리를 해야겠다.
// Thread 상속
public class ThreadWithClass extends Thread{
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName()); // 현재 실행 중인 스레드의 이름을 반환
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// Runnable 구현
public class ThreadWithRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()); // 현재 실행 중인 스레드의 이름을 반환
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 스레드 사용
public class Main {
public static void main(String[] args) {
ThreadWithClass thread1 = new ThreadWithClass();
Thread thread2 = new Thread(new ThreadWithRunnable());
thread1.start();
thread2.start();
// 람다식을 이용한 스레드 구현
Runnable task = () -> {
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread3 = new Thread(task, "Thread3");
thread3.start();
}
}
람다
람다(lambda)는 익명 함수를 의미한다.
해당 인터페이스의 메서드가 1개 이상이면 람다 함수를 사용할 수 없다.
그래서 람다 함수로 사용할 인터페이스는 @FunctionalInterface 어노테이션을 사용해서 2개 이상의 메서드를 가지 못하도록 만들어 주는 것이 좋다고 한다.
interface Calculator {
int sum(int a, int b);
}
class MyCalculator implements Calculator {
public int sum(int a, int b) {
return a+b;
}
}
public class Sample {
public static void main(String[] args) {
MyCalculator mc = new MyCalculator(); // 일반적인 코드 사용
// (매개변수목록) -> { 함수몸체 }
//Calculator mc = (int a, int b) -> a +b; // 람다를 적용한 코드
//Calculator mc = Integer::sum; // 메서드를 사용할 때는 :: 기호로 축약해서 사용이 가능하다
int result = mc.sum(3, 4);
System.out.println(result); // 7 출력
}
}
스트림
원본 데이터를 변경하지 않는다.
일회성으로 작동이 된다.
스트림을 생성 -> 스트림 변환 -> 스트림 최종 값연산
import java.util.Arrays;
import java.util.Comparator;
public class Sample {
public static void main(String[] args) {
int[] data = {5, 6, 4, 2, 3, 1, 1, 2, 2, 4, 8};
int[] result = Arrays.stream(data) // IntStream을 생성한다.
.boxed() // IntStream을 Stream<Integer>로 변경한다.
.filter((a) -> a % 2 == 0) // 짝수만 뽑아낸다.
.distinct() // 중복을 제거한다.
.sorted(Comparator.reverseOrder()) // 역순으로 정렬한다.
.mapToInt(Integer::intValue) // Stream<Integer>를 IntStream으로 변경한다.
.toArray() // int[] 배열로 반환한다.
;
}
}
4,5 주차 내용들은 복잡한 부분들이 많아서 다른 강의도 보면서 좀 더 익숙해져야 할 것 같다.