그림으로 배우는 자바(4)

그림으로 배우는 자바 - 패키지, 예외처리

13 대규모 프로그램의 작성

클래스 여러 개를 다른 파일에 나누어 작성하는 방법

ex) Car.java -> Car 클래스

Sample1.java -> main() 메소드를 가지고 있는 Sample1 클래스

Sample1의 컴파일 방법 javac Sample1.java 그 결과 Sample1.class , Car.class 2개의 파일이 생성된다. 프로그램을 실행시킬 때에는 생성된 2개의 클래스 파일을 같은 폴더에 위치시키고 다음과 같이 입력. java Sample1

패키지의 원리 이해하기

-> 클래스 이름이 충돌하는 것을 방지. 클래스에 패키지를 포함시키기 package 패키지 이름;

컴파일 방법 javac pa\Sample2.java

폴더 이름 \소스 파일 이름을 입력해서 컴파일 한다. 이후 작업폴더에서 java pa . Sample2로 입력해서 실행한다.

정리하자면

  1. 패키지 이름과 같은 폴더를 작업 디렉토리 아래에 만드나. 그리고 그 곳에 소스 파일을 저장한다
  2. 작업중인 폴더에서 다음 명령을 사용하여 컴파일 한다. javac 폴더 이름\소스 파일 이름
  3. 작업 중인 폴더에서 javac 패키지 이름 클래스 이름을 입력해서 실행시킨다.

같은 패키지 안의 클래스 사용하기

동일한 패키지에 포함만 시켜주면 된다. 패키지를 지정하지 않으면, 그 해당 클래스는 ‘이름없는 패키지’에 포함되게 된다.

다른 패키지 안의 클래스 사용하기 - 아래 두 번의 작업이 필요

  1. 다른 패키지의 클래스에서 사용될 클래스의 이름 앞에 public 붙이기
  2. 다른 패키지의 클래스를 사용할 때 그 클래스의 이름 앞에 패키지 이름 붙이기
즉 정리하자면 클래스에 붙이는 public

-> 다른 패키지의 클래스가 사용할 수 있다. 반대로 public을 생략하면 같은 패키지에 속한 -> 클래스들만 사용할 수 있는 클래스. 정리 ##### 클래스 및 인터페이스에 붙이는 제한자

  • 지정하지 않음 : 같은 패키지에서만 사용하겠다.
  • public 다른 패키지에서도 사용할 수 있게 하겠다.
멤버 및 생성자에 붙이는 제한자
  • private : 같은 클래스 안에서만 접근할 수 있도록 하겠다.
  • 지정하지 않음 : 같은 패키지에서만 접근할 수 있도록 하겠다.
  • protected: 같은 패키지에 속한 클래스와 다른 패키지에 속한 서브클래스만 접근할 수 있도록 하겠다.
  • public: 모든 클래스가 접근할 수 있도록 하겠다. (단 클래스가 public이 아닌 경우, 같은 패키지에서만 접근 가능)

패키지 이름으로 클래스 구분하기

-> 패키지에 의해 나뉘어진 클래스 이름의 모임을 일컬어 네임스페이스라고 부른다.

임포트

다른 패키지에 속한 클래스의 이름 앞에 매번 패키지의 이름을 붙이는 건 번거로운 일. 그럴 때 파일의 앞머리에서 임포트라는 작업을 해줄 수 있다. import 패키지 이름.클래스 이름;

import pc.Car;
car car1 = new Car();

서브 패키지 만들기

패키지 안에 패키지를 만들 수 있다. - > 서브 패키지 패키지로 계층 구조를 만들면 많은 클래스를 기능 별로 분류할 수 있다. 계층 구조를 다음 처럼 만든다고 가정 13폴더 -> pa / pb/ pc -> pa 폴더 안에 sub 폴더 -> 그 안에 Sample7.java 달라진 점은 서브 패키지 이름의 앞 뒤에 마침표를 찍어 구분해준다. ex) java.pa.sub.Sample7 서브 패키지를 만들면 비슷한 역할을 담당하는 클래스들을 종류별로 분류할 수 있다. 주의할 점은 Java는 패키지(pa)와 서브 패키지(pa.sub)를 완전히 다른 패키지로 간주한다.

클래스 라이브러리의 패키지

java 클래스 라이브러리 또한 패키지로 분류되어 관리가 되고 있다. 단 java.ang 패키지만 예외적으로 임포트가 필요없다. 클래스 이름만 적어서 사용하다. 그래서 이 패키지에 포함된 String 클래스를 별도의 임포트 작업 없이 사용 가능 했던 것.

패키지 안의 클래스 모두 임포트하라

import java.io.*; 단 이 표현은 서브패키지의 클래스들까지는 읽어들이지 않는다. 따라서 서브패키지도 가져오려면 각각 import문을 써줘야 한다.

예외와 입출력 처리

컴파일 할 때 찾을 수 없는 오류들이 있어. 파일을 못찾거나, 문자열을 정수로 변환할 때, 정수로 변환될 수 없는 문자열을 입려가거나, 배열의 길이를 넘어서는 값에 값을 대입시켰 다던가. java는 이러한 발생 오류에 대응하기 위해 ‘예외처리’ 메커니즘 사용.

예외 처리하기

예외처리 (exception handling)

try {
    예외 발생을 조사할 문장(statement)
}
catch(예외 클래스 변수명){
    예외가 발생했을 때 실행시킬 코드;
}

실행 순서

  1. try 블록 안에서 예외가 발생하면 그 시점에서 코드의 실행을 중단한다.
  2. 발생된 예외의 종류가 catch 블럭의 () 안에서 지정한 예외와 일치하면 그 안의 코드를 실행시킨다.
  3. catch 블록 안의 코드가 모두 실행되면 그 try~catch 블록 다음에 등장하는 코드를 실행시킨다. 던져진(throw) 예외의 종류와 catch 블록 ( ) 안의 예외가 일치할 경우 그 catch블록 안의 코드가 실행된다. 이를 일컬어 catch 블록에서 예외를 받는다(catch)라고 부른다.

finally 블록 추가하기

이 블록의 예외 처리 붙이면 다음과 같은 모습 실행

try{
    예외 발생을 조사할 문장
}
catch(예외 클래스 변수명) {
    예외가 발생했을 때 실행시킬 코드;
}
finally{
    마지막에 반드시 실행시켜야 하는 코드;
}

finally블록은 예외 발생 유무에 관계없이 finally 블록이 선언된 메소드의 마지막에 반드시 실행된다. (finally 블록을 사용하면 예외 발생 유무에 관계없이 그 메소드 내에서 반드시 실행하고 싶은 중요한 프로세스 실행 가능. 예외가 발생하면 중간에 종료되버리니까. 예를 들어 파일 쓰기 작업이나 네트워크 연결 종료 처리 같은 것들이 있을 수 있다.)

예외와 클래스

예외란 사실 Throwable 클래스를 확장한 서브 클래스의 객체를 뜻한다. 예를 들어 우리가 지금까지 다루었던 예외는

Throwable 클래스의 서브 클래스를 확장한 ArrayIndexOutOfBoundsException이라는 클래스의 객체

예외의 종류 파악하기

java에는 다양한 종류의 예외 클래스 존재. Throwable 클래스는 Error 클래스와 Exception 클래스의 슈퍼 클래스이다. Error 클래스는 더이상 프로그램을 실행시킬 수 없음을 나타낸다.

예외 클래스 던지기

예외를 발생시키는 코드 작성 가능. 자신만의 예외 클래스를 만드는 방법. Throwable 클래스의 서브 클래스를 확장한 예외 클래스를 선언해야함.

ex)

class CarExcpetion extends Exception

예외 던지기

public void setCar (int n , double g) throws Carexception
{
    if(g<0){
        CarException e = new CarException(); // 예외 객체 생성
        throw e; // 생성된 예외 객체를 던진다.
    }
}

q) 예외를 받아내지 않으면 ? -> 프로그램이 실행 도중에 종료되버린다. 즉 예외가 발생할 가능성이 있는 경우 2가지 처리 가능 ① try ~ catch를 사용하여 해당 메소드에서 예외를 처리해 버리기② 메소드 이름 뒤에 throws를 붙여서 그 메소드의 호출 메소드에 예외 처리를 위임하기

입출력의 기본

생략

스레드

Java는 하나의 코드가 처리 흐름을 여러 개 가질 수 있도록 지원한다. 각각의 처리 흐름을 일컬어 스레드라고 한다.

스레드 기동시키기

class Car extends Thread
{
    public void run()
    {
        다른 스레드가 수행할 작업;
    }
}

Thread 클래스를 확장한 클래스는 반드시 그 안의 run( ) 메소드를 정의해야한다. 이 메소드에 작성된 코드는 지금까지와는 다른 처리 흐름에서 동작하며 run()메소드가 그 시작점이 된다. 예제를 보겠다.

class Car extends Thread
{
    private String name;
    
    public Car(String nm)
    {
        name = nm;
    }
    public void run()
    {
        for(int i=0; i <5;  i++){
            System.out.println(name + "가 동작하고 있습니다.");
        }
    }
}
class Sample1
{
    public static void main(String[] args)
    {
        Car car1 = new Car("1호차");
        car1.start();
        for(int i = 0 ; i < 5; i ++){
            System.out.println("main() 메소드 실행중입니다.");
        }
    }
}

main 메소드 안에서 Thread 클래스를 상속받은 Car클래스의 객체를 생성하고 있다. 그 다음 start() 메소드를 호출한다. -> 쓰레드를 가동.

start() 메소드는 Car클래스가 Thread 클래스로부터 상속 받은 메소드이다. 이 메소드를 호출하면

새로운 스레드를 가동시키고 그 스레드는 가장 먼저 run() 메소드를 실행시킬 것 이라는 매커니즘에 따라 작동하게 된다.

이 2개의 처리 순서는 정해져 있지 않다.

2개의 처리는 각자 별도의 순서에 따라 실행되기 때문이다.

즉 Java는 새로운 스레드를 기동시킬 경우 또 다른 처리 흐름을 만든다는 메커니즘을 가진다. 또한 새로 시작된 스레드는 run() 메소드의 종료와 함께 종료된다. 즉 본래 시작점이었던 main() 스레드는 지금까지와 마찬가지로 main() 메소드 종료시 함께 종료된다.

스레드의 상태 변화시키기

스레드 일시 정지시키기

ex ) sleep()메소드를 실행시키면 () 지정된 밀리세컨드 동안 스레드를 일시정지 시킨다.

스레드 종료를 기다리기

다른 스레드의 종료를 기다리게 만들고 다시 동작하는 것 또한 가능하다. 그러한 처리가 필요한 경우 join() 메소드를 사용한다.

스레드의 생성 방법

만약에 Thread 클래스를 상속 받을 클래스가 이미 다른 클래스를 상속 받았을 경우에는 어떻게 할까? Java는 두 개 이상의 클래스로부터 상속받을 수 없다. (다중 상속 금지) 이럴 때 클래스 라이브러리 Runnable 인터페이스(java.lang 패키지)를 사용하면 스레드로 만들 수 있는 클래스를 만들 수 있다. 즉 Thread 클래스를 확장하는 것이 아니라 Runnable 인터페이스를 구현하는 것이다.

class Car extends Vehicle implements Runnable
{
    ...
}

즉 Java에서 스레드를 만드는 방법에는

  1. Thread 클래스 확장하기
  2. Runnable 인터페이스 구현하기

두 가지 방법이 있는 것이다. Runnable 인터페이스로 구현한 겨우 main()메소드에서 Car 클래스의 객체를 생성한 다음 별도의 작업이 필요하다. 그것은 Thread 클래스의 객체 생성하기라는 작업이다.

Car car1 = new Car();
Thread th = new Thread(car1);
th.start();

동기화

동기화 문제를 해결하기 위해서

public synchronized void add(int a)
{
    ...
}

스레드와 스레드 사이의 처리 타이밍 메커니즘을 일커렁 동기화라고 부른다.


참고자료

그림으로 배우는 자바

0%