※ Bus 1대로 진행
※ Taxi 1대로 진행
package test1;
//상위 클래스 Transportation --> 추상 클래스
public abstract class Transportation {
    //필드
    int num;    //번호
    int currentGas; //주유량
    int acceleration;   //속도
    String status;  //상태
    
    //메소드 --> 추상 메소드: 앞에 abstract 을 붙임
    //추상 메소드
    abstract void board(int pass, String dest, int dis);    //탑승 board: 메소드 명,      승객 int pass, 목적지 String dest, 거리 int dis: 매개변수
    abstract void refuel();     //주유량 refuel: 메소드 명
    //문법: abstract 반환타입 메소드이름();
    abstract int refuel(int gas);       //refuel: 메소드 명 int gas: 필드명
    abstract int board(int pass);       //board: 메소드 명, int pass: 필드명
}
추상화 vs 상속
- 추상화: 클래스간의 공통점을 찾아내서 공통의 부모를 설계 --> 상향, 하향 상관없음
 - 상속: 상위 클래스를 사용하여, 하위 클래스를 정의 --> 하향
 
package test1;
import static test1.Taxi.gasLeft;
//클래스
public class Bus extends Transportation {
    //필드
    int maxPass = 30;		// 최대 승객 수
    int currentPass = 0;	// 현재 승객 수
    int cost = 1000;		// 요금
    
    //생성자
    //버스 번호 지정 [고유값으로 생성되어야 되기에 랜덤함수로 함]
    //버스 번호 (고유값으로 생성되어야 되기에 랜덤함수로. 랜덤함수는 기본형이 Double 형이기에 (int)로 정수화. 1부터 값을 뽑고 싶다면 +1 => 랜덤 함수는 0부터 나오기 때문에)
    public Bus() {          //Bus(): 기본 생성자, num: 상위 클래스의 필드명
        this.num = (int) (Math.random() * 100 + 1);     //Math.random() 메소드: 0.0 ~ 1에 무한히 가까운 수를 제공
        System.out.println("버스 번호: " + num);
    }
    //메소드
    // 버스 상태 변경
    boolean busStatus(boolean change) {     //boolean 메소드, busStatus: 메소드 명, boolean change: 매개변수(메소드 실행 시, 필요한 데이터를 받기 위한 변수)
        if(change == true)          //매개변수 chage가 true라면, 상위 클래스의 필드 status는 "운행중"이 된다
            status = "운행중";
        else {                      // 매개변수 chage가 false라면, 상위 클래스의 필드 status는 "차고지행", 하위 클래스의 필드 currentPass은 0, 하위 클래스의 필드 maxPass은 30이 된다
            status = "차고지행";
            currentPass = 0;
            maxPass = 30;
        }
        return change;
    }
    // 버스 현재 상태
    void currentBus() {     //void 메소드, currentBus: 메소드 명    --> void 이므로, 리턴값은 없음
        System.out.println("상태 = " + status);       //상위 클래스의 필드 status 를 출력
        System.out.println("주유량 = " + currentGas);      //상위 클래스의 필드 currentGas 를 출력
    }
    //오버라이드
    @Override
    void board(int pass, String dest, int dis) {        //상위 클래스에서 봤던 int pass, String dest, int dis 매개변수를 여기 하위 클래스에서 재정의(오버라이드)
    }
    // 주유량
    @Override
    void refuel() {     //refuel: 상위 클래스의 메소드 명
    }
    int refuel(int gas) {       //refuel: 상위 클래스의 메소드 명, int gas: 매개변수
        currentGas += gas;          //매개 변수 gas 를 더해서 상위 클래스의 필드 currentGas 로 값을 저장한다
        if(!gasLeft()) {        //import 에서 gasLeft 가 있기 때문에, gasLeft() 로 사용 가능. --> 하위 클래스 Taxi 에서 static boolean gasLeft()를 사용하므로
            status = "차고지행";    //gasLeft가 아니라면, 상위 클래스의 status는 "차고지행"
        }
        return currentGas;
    }
    boolean available() {       //available: 메소드 명 --> 이렇게 하위 클래스에서 새롭게 선언하기도 함
        //승객 탑승은 ‘최대 승객수’ 이하까지 가능
        return maxPass >= currentPass;      //maxPass:하위 클래스의 필드, currentPass: 하위 클래스의 필드 --> 최대 승객 수는 현재 승객 수 보다 크거나 같다
    }
    // 승객 탑승
    @Override
    int board(int pass) {       //board: 상위 클래스의 메소드 명, pass: 매개변수
        if(pass >= (maxPass-currentPass))       //maxPass:하위 클래스의 필드, currentPass: 하위 클래스의 필드 --> 승객 수는 최대 승객 수에서 현재 승객 수를 뺀 수 보다 크거나 같다면
            System.out.println("최대 승객 수 초과");   //"최대 승객 수 초과"를 출력
        else {          // 아니라면
            if(available()) {       //available: 하위 클래스 Bus 의 메소드 명 --> available 가 true 라면,
                currentPass += pass;        // 하위 클래스의 필드 currentPass 에 승객 수 pass 를 더하고, 탑승 승객 수, 잔여 승객 수, 요금 확인 을 출력한다
                System.out.println("탑승 승객 수 = "+pass+"명");
                System.out.println("잔여 승객 수 = "+(maxPass-currentPass)+"명");
                System.out.println("요금 확인 = "+(cost*pass));
            }
            if(!available()) {      //available 가 false 라면,
                System.out.println("최대 승객 수 초과");       //최대 승객 수 초과 를 출력한다
            }
        }
        return currentPass;
    }
}
import static test1.Taxi.gasLeft;
- 클래스 내의 모든 정적(static) 메소드를 import 하기 위함
- import 란?  다른 패키지 안의 클래스를 사용하기 위해서
- import static 란?  일반적인 import 와 달리, 해당 클래스의 정적 변수나 메소드를 패키지명과 클래스명 없이 약칭으로 쓸 수 있도록 해준다.
- gasLeft는 Taxi 클래스의 static 의 메소드명 이다. --> 이렇게 사용되면, gasLeft() 메서드는 클래스명 없이 사용 가능해짐
- 전역참조 문자인 *를 사용해서 모두 불러오기도 함
오버라이드
- 부모 클래스에 있는 메소드를 자식 클래스에서 재정의 하는 것
- 부모 클래스의 메소드를 자식클래스에서 동일한 이름으로 다시 재정의 하면, 부모클래의 메소드를 찾지 않고 자식 클래스의 메소드를 호출
import java.util.*;
- 한 패키지에서 여러 클래스를 사용하는 경우 클래스의 이름을 일일이 지정해주는 것보다 '패키지명.*'과 같이 하는 것이 편리
 - 단점: 하지만, import하는 패키지의 수가 많을 때는 어느 클래스가 어느 패키지에 속하는지 구별하기 어렵다
 
package test1;
public class testBus {
    public static void main(String[] args) {
        // 버스 테스트
        // 1번
        // 1~2. 버스 2대 생성 & 출력 --> 랜덤 번호 출력
        Bus bus1 = new Bus();       //객체 생성
        Bus bus2 = new Bus();
        // 2번 (버스 1대로 진행)
        // 1 ~ 2. 승객 +2 & 출력    //하위 클래스 Bus 의 int board 메소드 실행 --> if/else 中 else 문 실행 --> available()/!available() 中 true 인 available() 실행(available()메소드로 가서 true 인지 판단)
        bus1.board(2);          // --> 하위 클래스의 필드 currentPass 에 승객 수 pass 를 더하고, 탑승 승객 수, 잔여 승객 수, 요금 확인 을 출력
        // 3 ~ 4. 주유량 50        //하위 클래스 Bus 의 int refuel 메소드 실행 --> 매개 변수 gas 를 더해서 상위 클래스의 필드 currentGas 로 값을 저장
        bus1.refuel(50);        //--> return currentGas
        System.out.println("주유량 = "+bus1.currentGas);
        // 5. 상태 변경 => 차고지행         //하위 클래스 Bus 의 boolean busStatus 메소드 실행 --> 매개변수 boolean change 가 false라면, status = "차고지행"
        bus1.busStatus(false);         // --> return change
        // 6. 주유량 +10           //하위 클래스 Bus 의 int refuel 메소드 실행
        bus1.refuel(10);
        // 7. 버스 상태와 주유량 출력
        bus1.currentBus();
        // 8. 상태 변경 => 운행중
        bus1.busStatus(true);
        // 9 ~ 10. 승객 +45 => 최대 승객 수 초과         //하위 클래스 Bus 의 int board 메소드 실행
        bus1.board(45);
        // 11 ~ 12. 승객 +5 & 출력      //하위 클래스 Bus 의 int board 메소드 실행
        bus1.board(5);
        // 13. 주유량 -55          //하위 클래스 Bus 의 int refuel 메소드 실행 --> if(!gasLeft()) 이면, status = "차고지행"
        bus1.refuel(-55);
        // 14. 버스 상태와 주유량 출력        //하위 클래스 Bus 의 currentBus 메소드 실행 --> "상태 = " + status 와 "주유량 = " + currentGas 출력
        bus1.currentBus();
    }
}
package test1;
public class Taxi extends Transportation {
    //필드
    String destination;			// 목적지
    int distance;				// 목적지까지 거리
    int maxPass = 4;			// 최대 승객수
    int defaultDistance = 1;	// 기본 거리
    int defaultCost = 3000;		// 기본 요금
    int perDistance = 1000;		// 거리당 요금
    //정적(static) 필드(변수)
    static String status = "일반";		// 상태
    int speed = 0;				// 속도
    int total = 0;				// 누적 금액
    int cost;					// 승객이 지불할 금액
    //생성자
    // 택시 번호 지정 [고유값으로 생성되어야 되기에 랜덤함수로 함]
    public Taxi() {
        this.num = (int)(Math.random()*100+1);
        // 랜덤함수는 기본형이 Double 형이기에 (int)로 정수화
        // 1부터 값을 뽑고 싶다면 +1 => 랜덤 함수는 0부터 나오기 때문에
        System.out.println("택시 번호 : "+num);
        Taxi.drive();
    }
    
    //static 메소드: 메소드 명 앞에 static 을 붙임 --> 객체 생성없이 클래스를 통해 메서드를 직접 호출 가능
    static boolean drive() {
        if (!gasLeft()) {
            status = "운행 불가";
            System.out.println("주유 필요");
            return false;
        }
        return true;
    }
    static boolean gasLeft() {
        return false;
    }
    // 탑승		승객			목적지		거리
    @Override
    void board(int pass, String dest, int dis) {         //상위 클래스에서 봤던 int pass, String dest, int dis 매개변수를 여기 하위 클래스에서 재정의(오버라이드)
        if(status == "일반") {
            if(pass > 4)
                System.out.println("최대 승객 수 초과");
            else {
                if(dis==1)
                    cost = defaultCost+ (perDistance*dis);
                else
                    cost = defaultCost+ (perDistance*(dis-1));
                status = "운행중";
                System.out.println("탑승 승객 수 = "+pass);
                System.out.println("잔여 승객 수 = "+ (maxPass-pass));
                System.out.println("기본 요금 확인 = "+defaultCost);
                System.out.println("목적지 = "+dest);
                System.out.println("목적지까지 거리 = "+ dis+"km");
                System.out.println("지불할 요금 = "+cost);
                total += cost;
            }
        }
    }
    @Override
    void refuel() {
    }
    // 주유량
    @Override
    int refuel(int gas) {
        currentGas += gas;
        if(!gasLeft()) {
            status = "운행 불가";
        }
        return currentGas;
    }
    @Override
    int board(int pass) {
        return 0;
    }
    // 요금 지불
    int pay() {
        status = "일반";
        maxPass = 4;
        System.out.println("누적 요금 = "+ total);
        if(!gasLeft())
            System.out.println("주유 필요");
        cost = 0;
        return total;
    }
    void passenger(int pass) {
        if(pass > 4)
            System.out.println("최대 승객 수 초과");
    }
    // 속도변경
    int changeSpeed(int acceleration) {
        //주유 상태를 체크하고 주유량이 10 이상이어야 운행할 수 있음
        if(gasLeft()) {
            this.acceleration = acceleration;
            speed += acceleration;
            System.out.println("현재 속도는 "+ speed+"입니다.");
        }
        System.out.println("주유량을 확인해주세요."+currentGas);
        return currentGas;
    }
}
package test1;
public class testTaxi {
    // 정적(static) 메소드
    public static void main(String[] args) {
        //TODO Auto-generated method stub
        // 택시 테스트
        // 1번
        // 1~2. 버스 2대 생성 & 출력
        Taxi taxi1 = new Taxi();        //객체 생성
        Taxi taxi2 = new Taxi();
        System.out.println("taxi1 주유량 = "+taxi1.currentGas);
        System.out.println("taxi1 상태 = "+taxi1.status);
        System.out.println("taxi2 주유량 = "+taxi2.currentGas);
        System.out.println("taxi2 상태 = "+taxi2.status);
        //2번(Taxi 1대로 진행)
        // 1~2.승객+2 목적지 = 서울역 목적지까지 거리 2km & 출력
        taxi1.board(2, "서울역", 2);
        System.out.println("상태 = "+ taxi1.status);
        // 3. 주유량 -80
        taxi1.refuel(-80);
        // 4~5. 요금결제 & 출력
        System.out.println("주유량 = "+taxi1.currentGas);
        taxi1.pay();
        // 6~7. 승객+5 & 최대승객수 초과
        taxi1.passenger(5);
        // 8~9. 승객+3 목적지 = 구로디지털단지역 목적지까지 거리 12km & 출력
        taxi1.board(3, "구로디지털단지역", 12);
        // 10. 주유량 -20
        taxi1.refuel(-20);
        // 11~12. 요금 결제 & 출력
        System.out.println("주유량 = "+taxi1.currentGas);
        taxi1.pay();        // 하위 클래스 Taxi 의 int pay() 메소드 실행
    }
}