김미썸코딩

11/18 - Java (8) : 타입변환과 다형성, 중첩클래스, 익명 객체, 예외 본문

빅데이터 플랫폼 구축을 위한 자바 개발자 양성과정

11/18 - Java (8) : 타입변환과 다형성, 중첩클래스, 익명 객체, 예외

김미썸 2020. 11. 23. 08:45
728x90

상속에 있어서 부모가 될수 있는 것

1. 일반클래스

          내용이 다 채워진 메서드 => 메서드(){} 이렇게 {}만 있으면 채워진거다.

2. 추상클래스

          추상메서드를 가질 수 있다.

          abstract 메서드(); => {}를 써서 채울수 없다. 

 

          * extends, 오버라이드

 

* interface

          abstract 메서드();

          * implements, 오버라이드

 

 

▷p330 매우 중요

추상화를  통해서 나오는 개념 - 다형성 ( 형변환에서 나옴)

 

형변환

          자동 형변환               - 작은 자료형 -> 큰 자료형

          강제 형변환               - 큰 자료형 -> 작은 자료형

         * 객체 -  (상속을 전제로함)

                   자동 형변환              - 자식 ->  부모

                   강제 형변환              - 부모 ->  자식

 

OOP

         캡슐

         상속 

                     추상

                     다형

 

 

 

 

 


 

 

 

 

상속  >  타입 변환과 다형성


▷p305

1. 타입변환 ( 객체 )

 

다형성을 위해 자바는 부모 클래스로 타입 변환을 허용한다.

즉, 부모 타입에 모든 자식 객체가 대입될 수 있다.

  • 특정 자식을 통해서 만들어진 부모는 그 자식으로만 강제 형변환 될수 있다.
  • 모든 클래스는 Object 로 자동 형변환이 가능하다.
class Parent{
    Parent(){
        System.out.println("Parent 생성자");
    }
}

class Child extends Parent{
    Child(){
        System.out.println("Child 생성자");
    }
}

public class PolyEx01{
    public static void main(String[] args){
        Parent p = new Parent();
        Child c1 = new Child();

        Child c2 = c1;  // 얕은 복사
        System.out.println(c2);
        System.out.println(c1);

        // 자동 형변환
        Parent p2 = c1; // 부모 형변환
        System.out.println(p2);
        System.out.println(c1);
        Parent p3 = new Child();

        // 모든 클래스 Object로 자동형변환 가능
        Object o1 = new Child();
        Object o2 = new Parent();

        // 강제 형변환
        Child c3 = (Child)p2;
        
        // Child c4 = (Child)p; // 에러남
        // Child c5 = (Child)new Parent(); // 에러남
        
        // 반드시 자식을 통해서 만든 것만 자식으로 강제 형변환 될수 있다.
        // 특정 자식을 통해서 만들어진 부모는 그 자식으로만 강제 형변환 될 수 있다.
        // p : Parent로 만들엇고,
        // p2 : Child로 만들어서 
        // p2만 Child로 강제 형변환이 가능하다.
    }
}

 

1-1. 자동 타입 변환

원래 작은 타입에서 큰 타입으로의 변환을 말하지만

객체의 상속에서 자식 클래스가 부모클래스보다 작으므로 자식 클래스에서 부모클래스로의 형변환은 자동 타입 변환이다.

Cat cat  = new Cat();
Animal animal = cat;

위에서 cat과 animal은 같은 Cat객체를 참조한다. 같은 주소값을 가진다.

자동 타입 변환이 일어난 것이다.

 

바로 위의 부모가 아니더라도 상속 계층에서 상위 타입이라면 자동 타입 변환이 일어날 수 있다. 

 

부모타입으로 자동 타입 변환된 후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능하다.

비록 변수는 자식 객체를 참조하지만 변수로 접근 가능한 멤버는 부모 클래스 멤버로만 한정된다. 

그러나 예외는, 메소드가 자식 클래스에서 오버라이딩 되었다면 자식 클래스의 메소드가 대신 호출된다.

 

 

 

 

 

다음을 코드를 보자.

class A{}

class B extends A{}
class C extends A{}

class D extends B{}
class E extends C{}

public class PromotionExample {
    public static void main(String[] args){
        B b = new B();
        C c = new C();
        D d = new D();
        E e = new E();

        A a1 = b;
        A a2 = c; 
        A a3 = d;
        A a4 = e;

        B b1 = d;
        C c1 = e;

        // 컴파일 에러 - 상속관계에 있지 않음
        // B b3 = e; 
        // C c2 = d;
    }
}

위 코드에서 D 객체는 B와 A타입으로 자동 타입 변환될 수 있고, E객체는 C와 A타입으로 자동형변환 될수 있다.

그러나 D객체는 C타입으로 변환될 수 없고 E객체는 B타입으로 변환 될수 없다. 상속 관계가 아니기 때문이다.

 

자식을 통해서 부모를 만들면 자식 고유의 것엔 접근할 수 없다.

 

부모 메서드가 자식에서 오버라이드 되면 부모 메스드를 호출시 오버라이드한 자식 메서드가 호출된다.

Parent p1 = new Child();

이렇게 자식을 통해 부모를 만들고

p1.viewParent2();	// Child에 오버라이드됨

메서드를 호출하면 오버라이드한 자식의 메서드가 호출된다.

 

 

 

 

 

1-2. 하나의 배열로 객체 관리 - 강제 형변환

강제 형변환으로 Object를 Car로 형변환 시키면, car.toString()으로 값 가져오는게 가능하다.

 

Car의 멤버 변수의 접근제한이 private이라서 다른 클래스에서 불러오지 못한다. 

즉, main에서 'car.멤버변수명' 으로 값을 불러오지 못한다.

따라서, Car에서 override한 toString()메서드를 사용한다.

class Car{
    private String name;
    private int numberOfWheels;
    private String color;

    Car(String name, int numberOfWheels, String color){
        this.name = name;
        this.numberOfWheels = numberOfWheels;
        this.color = color;
    }

    // override
    public String toString(){   // Object에 있는 toString()을 override
        return name + ":" + numberOfWheels + ":" + color;
    }
}

public class PolyEx03{
    public static void main(String[] args){
        Car[] cars = new Car[3];

        Car car1 = new Car("test1", 3, "Green");
        Car car2 = new Car("test2", 4, "Yellow");
        Car car3 = new Car("test3", 8, "Blue");

        cars[0] = car1;
        cars[1] = car2;
        cars[2] = car3;

        // 향상된 for 문
        for(Car car : cars){
            System.out.println(car.toString());
            // System.out.println(car); // 같다.
        }

        Object[] objs = new Car[3];

        objs[0] = car1;
        objs[1] = car2;
        objs[2] = car3;

        // 향상된 for문
        for(Object obj : objs){
            Car car = (Car)obj;
            System.out.println(car.toString());
            System.out.println(obj.toString());
        }
    }
}

 

 

 

2. 다형

부모를 통해서 자식 메서드를 호출 하는것.

abstract, interface, 상속은 모두 다형성이라는 객체지향 프로그래밍의 특징을 구현하는 방식이다.

animal = new Dog();
animal.sound();
animal = new Cat();
animal.sound(); 

이 두개는 다른 sound()를 불러온다. 이게 바로 다형성이다.

 

 

abstract는 new로 못만드는데 자식을 통해서 만드는 건 가능하다.

 

 

 

2-1. abstract 

다형성은 abstract 로도 구현된다.

부모클래스를 abstract화 한다.

// class Employee{
//     void salary(){
//         System.out.println("Employee salary()");
//     }
// }


// 위의 주석처럼 하면 밑에서 메서드를 다른이름으로 쓸수도 있으니까
// abstract를 사용
abstract class Employee{    
    abstract void salary();  // 상속받으면 반드시 구현해야함
}

class ChildEmployee1 extends Employee {
    void salary(){
        System.out.println("임원 salary()");
    }
}

class ChildEmployee2 extends Employee{
    void salary(){
        System.out.println("정직원 salary()");
    }
}


public class PolyEx04{
    public static void main(String[] args){

        // 기본 호출 방법
        ChildEmployee1 ce1 = new ChildEmployee1();
        ChildEmployee2 ce2 = new ChildEmployee2();
        ce1.salary();
        ce2.salary();

        // 다형성
        Employee e1 = new ChildEmployee1();
        Employee e2 = new ChildEmployee2();
        e1.salary();
        e2.salary();
    }
}

 

 

 

2-2. interface 

▷p365

다형성은 interface로도 구현된다.

부모클래스를 interface 로 두면 된다.

 

interface Tire{
    void roll();
}

class HankookTire implements Tire{
    @Override
    public void roll(){
        System.out.println("한국 타이어가 굴러갑니다.");
    } 
}

class KumhoTire implements Tire{
    @Override
    public void roll(){
        System.out.println("금호 타이어가 굴러갑니다.");
    }
}


// Tire 객체를 만드는 클래스
class Car{  
    Tire frontLeftTire = new HankookTire();
    Tire frontRightTire = new HankookTire();
    Tire backLeftTire = new HankookTire();
    Tire backRightTire = new HakookTire();

    public void run(){
        frontLeftTire.roll();
        frontRightTire.roll();
        backLeftTire.roll();
        backRightTire.roll();
    }
}

public class CarExample{
    static void main(String[] args){
        Car mycar = new Car();

        myCar.run();

		// 자식인 KumhoTire로 다시 만듬
        myCar.frontLeftTire = new KumhoTire();
        myCar.frontRightTire = new KumhoTire();

        myCar.run();
    }
}

 

 

1) 객체 타입 확인 ( instanceof )

▷p375

~로 인스턴스화 됐니? ~로 만들어졌니? 부모가 자식으로부터 만들어졌니? 처럼

출신을 알아볼수 있다.

모든 상속, 포함 관계를 알수 있고, 만약 부모 클래스가 자식 클래스로 만들어 진다면

부모  instanceof 자식  은  true 가 된다.

interface Vehicle{

}

class Bus implements Vehicle{

}

class Taxi{

}

public class PolyEx05 {
    public static void main(String[] args){
        Vehicle vehicle = new Bus();
        Taxi taxi = new Taxi();

        // class , abstract class, interface
        // 출신을 알아볼수 있다.
        // 모든 상속, 포함 관계를 알수 있고,
        // 만약 부모 클래스가 자식 클래스로 만들어 진다면
        // 부모 instanceof 자식  ->  true
        // 가된다.
        System.out.println(vehicle instanceof Bus);
        System.out.println(vehicle instanceof Vehicle);
        System.out.println(vehicle instanceof Object);

        System.out.println(taxi instanceof Vehicle);
    }
}

 

 

 

 


 

 

 

 

 

 

 

Eclipse


 

1. Eclipse 사용법

1-1. getter, setter 자동 생성

오른쪽 클릭 >  Source > Generator getters and setters > select all > generate

하면 만들어짐.

 

 

1-2. 생성자 자동 생성

1) Ca 치고 'Ctrl + space' 해서 constructor 클릭

2) 우클릭 > Source > generate constructor using field  클릭 > 초기화(생성자 만들)시킬 것들나오는데 밑에서 omit뭐시키 체크 하고 완료

 

 

 

1-3. toString() 자동 생성

우클릭 >  Source > to string > generate클릭하면 toString()만들어짐

 

 

1-4. 코드를 다른 곳에 가져가고 싶을 때 

workspace > 프로젝트 통째로 가져가라

 

 

1-5. 프로젝트 삭제 후 다시 불러오기

import > general > exist뭐시기 > 워크스페이스 선택 > 폴더선택 > 프로젝트나옴 > 피니쉬 하면 자동으로 프로젝트 등록이됨

 

만약 프로젝트 'JavaEx02' 를 삭제한다고 가정하면,

이렇게 삭제 후, 

 

 

다시 불러오기

 

다시 가져온 것을 확인 가능

 

2. 상속 

 

1. override

 

클래스 만들 때 Superclass 에서 Browser눌러서 부모클래스로 할거 선택

 

 

override

 

 

 

2. abstract

클래스 안에 아무것도 선언되어 있지 않으면 클래스명에 빨간줄 뜬다. 

거기에 마우스 대면 

이렇게 창뜨고 거기서 'Add unimplemented methods' 를 선택하면 자동으로 만들어진다.

 

 

 

 

 

 

 

 

 

3. interface

 

interface는 클래스 만들기가 아니라 만드는게 따로 있다.

 

 

 

일반 클래스에서 인터페이스를 쓰려면 

 

interfaces를 add해야한다.

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

중첩 클래스와 중첩 인터페이스


▷p390

 

** 중첩을 하는 이유 : 그 안에서만 임시적으로 사용한다.

 

 

1. 중첩 클래스 

클래스가 여러 클래스와 관계를 맺는 경우에는 독립적으로 사용하는 것이 좋으나 ,

특정 클래스와 관계를 맺을 경우에는 관계 클래스를 클래스 내부에 선언하는 것이 좋다.

중첩클래스란 클래스 내부에 선언한 클래스를 말하는데 , 중첩클래스를 사용하면 두 클래스 멤버들을 서로 쉽게  접근할 수 있다는 장점과 외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.

로컬 클래스(중첩 클래스)는 메소드 실행시에만 사용되고, 메소드가 실행 종류되면 없어진다.

 

 

 중첩 클래스의 종류

 

 

1-1. 인스턴스 멤버 클래스

인스턴스 멤버 클래스는 인스턴스 필드와 메서드만 선언이 가능하고 정적 필드와 메서드는 선언할 수 없다.

 

1) 객체 생성과 메서드 호출

Outer o = new Outer();
Outer.Inner oi = o.new Inner();

oi.viewInner();

먼저 Outer 객체를 생성하고 Inner 클래스의 객체를 생성해야 한다. 

Outer객체가 먼저 인스턴스화가 되어야 Inner클래스가 선언되기 때문이다.

viewInner()메서드를 사용한후,  Inner클래스는 없어진다. 

 

 

2) 예시

class Outer {
    
    // 멤버필드
    private int x1 = 100;
    public int x2 = 100;

    Outer(){
        System.out.println("Outer 생성자");
    }
    
    // 인스턴스 멤버 클래스 
    class Inner{
        private int y1 = 200; 
        public int y2 = 200;

        Inner(){
            System.out.println("Inner 생성자");
        }
        public void viewInner(){
            System.out.println(x1);
            System.out.println(x2);
            System.out.println(y1);
            System.out.println(y2);
        }
        
    }
}

public class NestedEx01{
    public static void main(String[] args){
        Outer o = new Outer();
        // 중첩 클래스 생성
        Outer.Inner oi = o.new Inner();

        // System.out.println(o.x1);
        System.out.println(o.x2);

        oi.viewInner();
    }
}

 

 

 

 

1-2. 정적 멤버 클래스

정적 멤버 클래스는 모든 종류의 필드와 메서드를 선언할 수 있다.

 

1) 객체 생성과 메서드 호출

Outer.Inner oi = new Outer.Inner();

Outer.Inner.viewInner();

 

static은 객체가 생성되기 전의 개념이므로 

일반적인 static 메서드를 부를때처럼, '외부클래스명.static클래스명.static메서드명' 으로 호출한다.

 

 

2) 예시

class Outer {
    
    // 멤버필드
    private int x1 = 100;
    public int x2 = 100;

    Outer(){
        System.out.println("Outer 생성자");
    }
    

    // static 멤버 ㅋ클래스
    static class Inner{
        private int y1 = 200; 
        public int y2 = 200;

        Inner(){
            System.out.println("Inner 생성자");
        }
        public void viewInner(){
            // System.out.println(x1);  // static이여서 안됨
            // System.out.println(x2);
            System.out.println(y1);
            System.out.println(y2);
        }
        
    }
}

public class NestedEx02{
    public static void main(String[] args){
        // Outer o = new Outer();

        Outer.Inner oi = new Outer.Inner();
        Outer.Inner.viewInner();
    }
}

 

 

1-3. 로컬 클래스

메서드가 실행 될 때 메서드 내에서 객체를 생성하고 사용해야 한다.

 

1) 객체 생성과 메서드 호출

Outer o = new Outer();

o.viewInner();

따로 객체를 만들지 않아도 Outer 객체를 만들고 Outer객체의 메서드를 호출하면 

해당 메서드 안의 Inner 클래스가 객체를 생성하고 사용한다. 

 

2) 예시

 

o.viewInner()로 

class Outer 안의 메서드인 viewInner()가 호출된다.

 

viewInner안의 

Inner i =  new Inner();
i.view();

위의 코드가 실행되면서 

class Inner 안의 view()메서드를 실행 시킨다.

 

 

 

 

 

 

 

 

 

 

 

 

class Outer {
    void viewInner(){
        // 선언
        class Inner{
            void view(){
                System.out.println("view 호출");
            }
        }

        Inner i =  new Inner();
        i.view();
    }
}

public class NestedEx03{
    public static void main(String[] args){
        Outer o = new Outer();
        o.viewInner();
    }
}

 

 

 

 


 

 

 

 

 

 

 

 

익명 객체


▷p404

익명 객체는

interface Inner{
    int x = 100;
    void viewInner();   // 내용을 구현하지 않고 추상메소드 선언
}

public class NestedEx04{
    public static void main(String[] args){
        
        int y = 100;
        // 익명 inner 클래스
        Inner i =  new Inner(){
            public void viewInner(){
                System.out.println( x );
                // 지역변수에 접근 가능
                System.out.println( y );
            }
        };

        i.viewInner();
        
    }
}

 

 

 

AnonymousExample.java

 

interface Calculatable{
    int sum();    
}

class Anonymous{
    private int field;

    public void method(final int arg1, int arg2){
        final int var1 = 0;
        int var2 = 0; 

        field = 10;

        Calculatable calc = new Calculatable(){
            @Override
            public int sum(){
                int result = field + arg1 + arg2 + var1 + var2;
                return result;
            }
        };

        System.out.println(calc.sum());
    }
}

public class AnonymousExample {
    public static void main(String[] args){
        Anonymous anony =  new Anonymous();
        anony.method(0,0);
    }
}

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

예외


▷p422

 

 

에러

 

컴파일 에러

               java -> class

               문법오류 - compiler가 확인

                * 오타 확인

런타임 에러

               class -> 실행

               값의 오류

               =>  if

               =>  Exception (고급에러 처리기법)  => 클래스 정의 

 

 

 

 

ExceptionEx01.java

 

public class ExceptionEx01{
    public static void main(String[] args){
        System.out.println("시작");

        // int num1 = 2;
        int num1 = 0;
        int num2 = 10; 

        if(num1 == 0){
            System.out.println("0으로 나눌 수 없습니다.");
        }else{
            int result = num2 / num1;
            System.out.println(result);
        }

        
        System.out.println("종료");
    }
}

 

 

잘못해서 int num1 = 0; 으로 해서 넘기면 컴파일시엔 오류가 안나는데 실행시에 ArithmeticException이라고 뜨면서 에러가 난다. 

나눗셈은 0으로 나눌수 없기 때문이다. 이렇게 컴파일시엔 에러가 발생하지 않고 실행시 발생하는 에러를 '실행시 에러' 라고 한다

 

 

 

 

 

 

ExceptionEx02.java

NullPointerException 은 대부분 초기화를 안해줘서 발생한다. 

public class ExceptionEx02{
    public static void main(String[] args){
        System.out.println("시작");

        String data = null;
        if(data == null){
            System.out.println("객체 사용 불가");
        }else{
            System.out.println(data.toString());
        }
       
        System.out.println("종료");
    }
}

 

 

 

try ~ catch 사용

ExceptionEx03.java

exception을 만나면 JVM이 Exception을 생성해준다. 이걸 getMessage()로 읽을 수 있다.

public class ExceptionEx03{
    public static void main(String[] args){
        System.out.println("시작");

        // int num1 = 2;
        int num1 = 0;
        int num2 = 10; 

        try{
            int result = num2 / num1;
            System.out.println(result);
        }catch(ArithmeticException e){
            // ArithmeticException e = new ArithmeticExciption();
            System.out.println("인셉션 발생 : " + e.getMessage() );
        }
        
        System.out.println("종료");
    }
}

 

 

 

 

ExceptionEx03.java

try~ catch~ final에러가 발생하든 안하든 무조건 실행시키고 싶은 것은 final을 쓴다.

public class ExceptionEx03{
    public static void main(String[] args){
        System.out.println("시작");

        // int num1 = 2;
        int num1 = 0;
        int num2 = 10; 

        try{
            int result = num2 / num1;
            System.out.println(result);
        }catch(ArithmeticException e){
            // ArithmeticException e = new ArithmeticExciption();
            System.out.println("인셉션 발생 : " + e.getMessage() );
        }
        
        System.out.println("종료");
    }
}

 

 

 

 

 

▷p429

public class TryCatchFinallyExample{
    public static void main(String[] args){
        try{
            Class clazz = Class.forName("java.lang.String2");
        }catch(ClassNotFoundException e){
            System.out.println("클래스가 존재하지 않습니다.");
        }
    }
}

 

public class TryCatchFinallyRuntimeExceptionExample{
    public static void main(String[] args){
        String data1 = null;
        String data2 = null;

        try{
            data1 = args[0];
            data2 = args[1];
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("실행 매개값의 수가 부족합니다.");
            System.out.println("[실행 방법]");
            System.out.println("java TryCatchFinallyRuntimeExceptionExample num1 num2");
            return;
        }

        try{
            int value1 = Integer.parseInt(data1);
            int value2 = Integer.parseInt(data2);
            int result = value1 + value2;
            System.out.println(data1 + "+" + data2 + "=" + result);
        }catch(NumberFormatException e){
            System.out.println("숫자로 변환할 수 없습니다.");
        }finally {
            System.out.println("다시 실행하세요.");
        }
    }
}
728x90
Comments