103. 상속 중 예외 검사에는 어떤 규칙이 적용됩니까?
질문을 올바르게 이해하면 상속 중 예외 작업 규칙에 대해 묻는 것입니다. 관련 규칙은 다음과 같습니다.- 하위 항목/구현에서 재정의되거나 구현된 메서드는 슈퍼클래스/인터페이스 메서드의 예외보다 계층 구조에서 더 높은 확인된 예외를 발생시킬 수 없습니다.
public interface Animal {
void speak() throws IOException;
}
이 인터페이스를 구현할 때 더 일반적인 발생 가능한 예외(예: Exception , Throwable )를 노출할 수는 없지만 기존 예외를 FileNotFoundException 과 같은 하위 클래스로 대체할 수 있습니다 .
public class Cat implements Animal {
@Override
public void speak() throws FileNotFoundException {
// Some implementation
}
}
- 하위 클래스 생성자의 throws 절 에는 객체를 생성하기 위해 호출된 슈퍼클래스 생성자에서 발생한 모든 예외 클래스가 포함되어야 합니다.
public class Animal {
public Animal() throws ArithmeticException, NullPointerException, IOException {
}
그런 다음 하위 클래스 생성자도 이를 발생시켜야 합니다.
public class Cat extends Animal {
public Cat() throws ArithmeticException, NullPointerException, IOException {
super();
}
또는 메서드와 마찬가지로 좀 더 일반적인 예외를 지정할 수 있습니다. 우리의 경우 Exception 을 표시할 수 있습니다 . 왜냐하면 Exception은 더 일반적이고 슈퍼클래스에 표시된 세 가지 예외 모두의 공통 조상이기 때문입니다.
public class Cat extends Animal {
public Cat() throws Exception {
super();
}
104. finally 블록이 실행되지 않는 코드를 작성할 수 있습니까?
먼저 마지막 이 무엇인지 기억해 봅시다 . 앞서 우리는 예외 포착 메커니즘을 조사했습니다. try 블록은 예외가 포착될 위치를 지정하고, catch 블록은 해당 예외가 포착될 때 호출될 코드입니다. finally 키워드 로 표시된 세 번째 코드 블록은 catch 블록을 대체하거나 뒤에 올 수 있습니다. 이 블록의 기본 개념은 try 또는 catch 블록에서 발생하는 상황에 관계없이(예외 발생 여부에 관계없이) 해당 코드가 항상 실행된다는 것입니다 . 이 블록이 실행되지 않는 인스턴스는 드물고 비정상입니다. 가장 간단한 예는 finally 블록 이전에 System.exit(0)이 호출되어 프로그램이 종료되는 경우입니다.try {
throw new IOException();
} catch (IOException e) {
System.exit(0);
} finally {
System.out.println("This message will not be printed on the console");
}
finally 블록이 실행되지 않는 다른 상황도 있습니다 :
-
예를 들어 심각한 시스템 오류로 인해 발생한 비정상적인 프로그램 종료 또는 애플리케이션 충돌을 일으키는 일부 오류(예: 애플리케이션 스택이 오버플로될 때 발생하는 StackOverflowError )입니다.
-
또 다른 상황은 데몬 스레드가 try-finally 블록 에 진입했지만 프로그램의 메인 스레드가 종료되는 경우입니다. 결국 데몬 스레드는 우선순위가 높지 않거나 필수가 아닌 백그라운드 작업을 위한 것이므로 애플리케이션은 작업이 완료될 때까지 기다리지 않습니다.
-
가장 무의미한 예는 try 또는 catch 블록 내부의 무한 루프입니다 . 일단 내부에 들어가면 스레드가 영원히 거기에 갇히게 됩니다.
try { while (true) { } } finally { System.out.println("This message will not be printed on the console"); }
105. 단일 catch 블록에서 여러 예외를 처리하는 예제를 작성하세요.
1) 이 질문이 올바르게 요청되었는지 확실하지 않습니다. 내가 아는 한, 이 질문은 여러 catch 블록과 단일 try를 참조하고 있습니다 .try {
throw new FileNotFoundException();
} catch (FileNotFoundException e) {
System.out.print("Oops! There was an exception: " + e);
} catch (IOException e) {
System.out.print("Oops! There was an exception: " + e);
} catch (Exception e) {
System.out.print("Oops! There was an exception: " + e);
}
try 블록 에서 예외가 발생하면 연결된 catch 블록은 위에서 아래로 순차적으로 예외를 잡으려고 시도합니다. 예외가 catch 블록 중 하나와 일치하면 나머지 블록은 더 이상 이를 catch하고 처리할 수 없습니다. 이는 모두 catch 블록 세트에서 더 좁은 예외가 더 일반적인 예외 위에 배열된다는 것을 의미합니다 . 예를 들어 첫 번째 catch 블록이 Exception 클래스를 포착하면 후속 블록은 확인된 예외를 포착하지 않습니다. 즉, Exception 하위 클래스가 있는 나머지 블록은 완전히 쓸모가 없습니다. 2) 아니면 질문이 올바르게 요청되었을 수도 있습니다. 이 경우 다음과 같이 예외를 처리할 수 있습니다.
try {
throw new NullPointerException();
} catch (Exception e) {
if (e instanceof FileNotFoundException) {
// Some handling that involves a narrowing type conversion: (FileNotFoundException)e
} else if (e instanceof ArithmeticException) {
// Some handling that involves a narrowing type conversion: (ArithmeticException)e
} else if(e instanceof NullPointerException) {
// Some handling that involves a narrowing type conversion: (NullPointerException)e
}
예외를 잡기 위해 catch를 사용한 후 , 객체가 특정 유형에 속하는지 확인하는 instanceof 연산자 를 사용하여 특정 유형을 찾으려고 합니다 . 이를 통해 부정적인 결과에 대한 두려움 없이 자신있게 축소 유형 변환을 수행할 수 있습니다. 동일한 상황에서 두 가지 접근 방식 중 하나를 적용할 수 있습니다. 나는 두 번째 옵션을 좋은 접근 방식이라고 부르지 않기 때문에 질문에 대해 의구심을 표명했습니다. 내 경험상 한번도 본 적이 없으며 여러 catch 블록을 포함하는 첫 번째 접근 방식이 널리 퍼져 있습니다.
106. 예외를 강제로 발생시킬 수 있는 연산자는 무엇입니까? 예시 작성
위의 예에서 이미 여러 번 사용했지만 다시 한 번 반복하겠습니다. 바로 throw 키워드입니다. 수동으로 예외를 발생시키는 예:throw new NullPointerException();
107. 메인 메소드가 예외를 던질 수 있나요? 그렇다면 어디로 가는 걸까요?
우선, 주요 메소드는 일반적인 메소드에 지나지 않는다는 점을 지적하고 싶습니다. 예, 프로그램 실행을 시작하기 위해 가상 머신에서 호출하지만 그 이상으로 다른 코드에서도 호출할 수 있습니다. 즉, throws 키워드 뒤에 확인된 예외를 표시하는 것과 관련된 일반적인 규칙도 적용됩니다 .public static void main(String[] args) throws IOException {
따라서 예외가 발생할 수 있습니다. main이 프로그램의 시작점으로 호출 되면 (다른 방법이 아닌), 발생하는 모든 예외는 UncaughtExceptionHandler
에 의해 처리됩니다 . 각 스레드에는 하나의 핸들러가 있습니다(즉, 각 스레드에 하나의 핸들러가 있습니다). 필요한 경우 자신만의 핸들러를 생성하고 public static void main(String[] args) throws IOException {setDefaultUncaughtExceptionHandler 메서드를 public static void main(String[] args) throws IOException {Thread 객체에서 호출하여 설정할 수 있습니다.
멀티스레딩
108. 멀티스레드 환경에서 작업하기 위한 어떤 메커니즘을 알고 있습니까?
Java의 멀티스레딩에 대한 기본 메커니즘은 다음과 같습니다.-
동기화 된 키워드는 스레드가 진입할 때 메소드/블록을 잠그고 다른 스레드가 진입하는 것을 방지하는 방법입니다.
-
휘발성 키워드 는 다른 스레드에서 액세스하는 변수에 대한 일관된 액세스를 보장합니다. 즉, 이 수정자를 변수에 적용하면 해당 변수를 할당하고 읽는 모든 작업이 원자성이 됩니다. 즉, 스레드는 변수를 로컬 메모리에 복사하여 변경하지 않습니다. 원래 값이 변경됩니다.
-
Runnable — 일부 클래스에서 이 인터페이스(단일 run() 메서드 로 구성됨 )를 구현할 수 있습니다.
public class CustomRunnable implements Runnable { @Override public void run() { // Some logic } }
해당 클래스의 객체를 생성하면 객체를 Thread 생성자에 전달한 다음 start() 메서드 를 호출하여 새 스레드를 시작할 수 있습니다 .
Runnable runnable = new CustomRunnable(); new Thread(runnable).start();
start 메소드는 별도의 스레드에서 구현된 run() 메소드를 실행 합니다 .
-
Thread — 이 클래스를 상속하고 해당 run 메서드를 재정의할 수 있습니다.
public class CustomThread extends Thread { @Override public void run() { // Some logic } }
이 클래스의 객체를 생성한 다음 start() 메서드를 호출하여 새 스레드를 시작할 수 있습니다.
new CustomThread().start();
- 동시성 — 다중 스레드 환경에서 작업하기 위한 도구 패키지입니다.
그것은 다음으로 구성됩니다:
-
동시 컬렉션 — 이는 다중 스레드 환경에서 작업하기 위해 명시적으로 생성된 컬렉션 컬렉션입니다.
-
대기열 — 다중 스레드 환경(차단 및 비차단)을 위한 특수 대기열입니다.
-
동기화 장치 — 다중 스레드 환경에서 작업하기 위한 특수 유틸리티입니다.
-
실행자 — 스레드 풀을 생성하는 메커니즘입니다.
-
잠금 — 표준 동기화 메커니즘(synchronized, wait, inform, informAll)보다 더 유연한 스레드 동기화 메커니즘입니다.
- Atomics — 멀티스레딩에 최적화된 클래스입니다. 각 작업은 원자적입니다.
-
109. 스레드 간 동기화에 대해 알려주세요. wait(), inform(), informAll() 및 Join() 메소드는 무엇입니까?
스레드 간 동기화는 동기화된 키워드에 관한 것입니다. 이 수정자는 블록에 직접 배치할 수 있습니다.synchronized (Main.class) {
// Some logic
}
또는 메소드 서명에서 직접:
public synchronized void move() {
// Some logic }
앞서 말했듯 이 동기화는 한 스레드가 진입하면 블록/메서드를 다른 스레드에 잠그는 메커니즘입니다. 코드 블록/메서드를 방으로 생각해보자. 어떤 실이 방에 접근하여 들어가고 열쇠로 문을 잠급니다. 다른 스레드가 방에 접근하면 문이 잠겨 있는 것을 확인하고 방을 사용할 수 있을 때까지 근처에서 기다립니다. 첫 번째 스레드가 방에서의 업무를 마치면 문을 열고 방을 나가며 열쇠를 놓습니다. 제가 몇 번이나 열쇠에 대해 언급한 데에는 이유가 있습니다. 비슷한 것이 실제로 존재하기 때문입니다. 이는 통화 중/한가함 상태를 갖는 특수 개체입니다. Java의 모든 객체에는 이러한 객체가 있으므로 동기화된 블록을 사용할 때 괄호를 사용하여 뮤텍스가 잠길 객체를 나타내야 합니다.
Cat cat = new Cat();
synchronized (cat) {
// Some logic
}
첫 번째 예제( Main.class ) 에서 했던 것처럼 클래스와 관련된 뮤텍스를 사용할 수도 있습니다 . 결국 메소드에서 동기화를 사용할 때 잠그려는 객체를 지정하지 않습니다. 그렇죠? 이 경우 비정적 메서드의 경우 잠길 뮤텍스는 this 개체, 즉 클래스의 현재 개체입니다. 정적 메서드의 경우 현재 클래스( this.getClass(); ) 와 연결된 뮤텍스가 잠겨 있습니다. wait()는 마치 현재 모니터(앵커와 같은 것)에 연결되는 것처럼 뮤텍스를 해제하고 현재 스레드를 대기 상태로 만드는 방법입니다. 이로 인해 이 메서드는 동기화된 블록이나 메서드에서만 호출될 수 있습니다. 그렇지 않으면 무엇을 기다리고 무엇을 출시할 것인가?). 또한 이는 Object 클래스 의 메서드라는 점에 유의하세요 . 글쎄, 하나가 아니라 세 개:
-
wait()는 다른 스레드가 이 객체에 대해 inform() 또는 informAll() 메서드 를 호출할 때까지 현재 스레드를 대기 상태로 전환합니다 (이러한 메서드에 대해서는 나중에 설명하겠습니다).
-
wait(long timeout)은 다른 스레드가 이 객체에 대해 inform() 또는 informAll() 메서드 를 호출하거나 timeout으로 지정된 시간 간격이 만료될 때까지 현재 스레드를 대기 상태로 전환합니다 .
-
wait(long timeout, int nanos)는 이전 방법과 비슷하지만 여기서 nanos를 사용하면 나노초(보다 정확한 시간 제한)를 지정할 수 있습니다.
-
inform()을 사용 하면 현재 동기화 블록에서 대기 중인 임의의 스레드 하나를 깨울 수 있습니다. 다시 말하지만, 이 메서드는 동기화된 블록이나 메서드 에서만 호출할 수 있습니다 (결국 다른 곳에서는 깨어날 사람이 없습니다).
-
informAll()은 현재 모니터에서 대기 중인 모든 스레드를 깨웁니다( 동기화 블록이나 메소드에서만 사용됨).
110. 쓰레드를 어떻게 멈추나요?
여기서 가장 먼저 말해야 할 것은 run()이 완료될 때까지 스레드가 자동으로 종료된다는 것입니다. 그러나 때로는 메서드가 완료되기 전에 일정보다 먼저 스레드를 종료하고 싶을 때도 있습니다. 그럼 우리는 무엇을 합니까? Thread 객체 에 stop() 메소드를 사용할 수 있을까요 ? 아니요! 해당 방법은 더 이상 사용되지 않으며 시스템 충돌을 일으킬 수 있습니다. 그럼 어쩌지? 이를 수행하는 방법에는 두 가지가 있습니다. 먼저 내부 부울 플래그를 사용합니다. 예를 살펴보겠습니다. 스레드가 완전히 멈출 때까지 화면에 특정 문구를 표시하는 스레드를 구현했습니다.public class CustomThread extends Thread {
private boolean isActive;
public CustomThread() {
this.isActive = true;
}
@Override
public void run() {
{
while (isActive) {
System.out.println("The thread is executing some logic...");
}
System.out.println("The thread stopped!");
}
}
public void stopRunningThread() {
isActive = false;
}
}
stopRunningThread() 메서드를 호출하면 내부 플래그가 false로 설정되어 run() 메서드가 종료됩니다. main 에서 호출해 봅시다 :
System.out.println("Program starting...");
CustomThread thread = new CustomThread();
thread.start();
Thread.sleep(3);
// As long as our main thread is asleep, our CustomThread runs and prints its message on the console
thread.stopRunningThread();
System.out.println("Program stopping...");
결과적으로 콘솔에 다음과 같은 내용이 표시됩니다.
public class CustomThread extends Thread {
@Override
public void run() {
{
while (!Thread.interrupted()) {
System.out.println("The thread is executing some logic...");
}
System.out.println("The thread stopped!");
}
}
}
메인 에서 실행 중 :
System.out.println("Program starting...");
Thread thread = new CustomThread();
thread.start();
Thread.sleep(3);
thread.interrupt();
System.out.println("Program stopping...");
이를 실행한 결과는 첫 번째 경우와 동일하지만 저는 이 접근 방식이 더 마음에 듭니다. 코드를 덜 작성하고 기성 표준 기능을 더 많이 사용했기 때문입니다. 자, 오늘은 여기까지입니다!
GO TO FULL VERSION