728x90

 

쓰레드의 생성방법은 크게 2가지가 있습니다. 어느 쪽을 선택해도 별 차이는 없지만 Thread 클래스를 상속받으면
다른 클래스를 상속받을 수 없기때문에, Runnable 인터페이스를 구현하는 방법이 일반적입니다.

Thread 클래스 상속 : java에서는 java.lang.Thread 클래스를 상속하여 쓰레드를 생성할 수 있습니다. Thread 클래스를 상속받은 사용자 정의 클래스를 만들고, run() 메서드를 오버라이딩하여 쓰레드가 실행할 작업을 정의합니다. 사용자 정의 클래스의 인스턴스를 생성한 후, statr( ) 메서드를 호출하여 쓰레드를 시작합니다.

 

     
     	//Thread class를 상속받아 run() 메서드 overrid한 클래스 정의( 또는 익명클래스)
        class MyThread extends Thread {
                @override 
            public void run() {
                // 쓰레드가 실행할 작업 정의
            }
        }

        // 쓰레드 객체 생성 및 실행
        MyThread thread = new MyThread(); 
        //또는 Thread thread = new Thread(); 
        //-> 이렇게 객체를 선언해도 어차피 Thread를 상속받은 상황이라 가능하다. 
        thread.start(); //statr()메서드를 이용하여 Thread 실행!

 

💡 Run() 메서드와 Start() 메서드

 

  1. run() 메서드 : run() 메서드는 Runnable 인터페이스나 Thread 클래스에서 구현해야 하는 메서드입니다. 쓰레드가 실행될 때 호출되는 메서드로, 쓰레드가 수행할 작업을 정의합니다. run() 메서드 내부에 작업의 로직을 구현하고, 이 메서드가 모두 실행되면 쓰레드의 실행이 종료됩니다. run() 메서드는 직접 호출하여 실행하는 것이 아니라, 쓰레드가 start() 메서드를 통해 실행될 때 내부적으로 호출됩니다.

  2. start() 메서드 : start() 메서드는 Thread 클래스에서 제공하는 메서드로, 쓰레드를 시작하는 역할을 합니다. start() 메서드를 호출하면, 쓰레드는 자신의 run() 메서드를 실행하기 위해 준비되고 , 내부적으로 새로운 실행 흐름을 만들고, 쓰레드가 독립적으로 실행될 수 있도록 준비를 합니다. start() 메서드를 호출한 후에는 별도의 작업이나 제어 없이 쓰레드가 자동으로 실행됩니다. start() 메서드는 한 번만 호출할 수 있습니다. 동일한 쓰레드에 대해 start() 메서드를 다시 호출하면 예외가 발생합니다.

 

Runnable 인터페이스 구현 : java에서는 java.lang.Runnable 인터페이스를 구현하여 쓰레드를 생성할 수 있습니다. Runnable 인터페이스를 구현한 사용자 정의 클래스를 만들고, 내부적으로 Runnable 객체의 run() 메서드를 구하여 쓰레드가 실행할 작업을 정의합니다.
사용자 정의 클래스의 인스턴스를 생성한 후 , Thread 클래스의 생성자에 Runnable 인터페이스를 구현한 객체를 전달합니다. Thread 클래스의 인스턴스의 start( ) 메서드를 호출하여 쓰레드를 시작합니다.

        class MyRunnable implements Runnable { 
        //Runnable interface를 구현한 클래스를 정의함
        //Runnable = Run()의 기능을 정의한 인터페이스(객체)
                @override
            public void run() {
                // 쓰레드가 실행할 작업 정의
            }
        }

        //Runnable  객체 생성 -> Threade 객체 생성 (생성자에 Runnable 객체를 전달)
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);

        //start() 메서드를 이용하여 Thread를 실행
        thread.start();

🚫 재정의한 메서드는 run() 이지만 Thread의 실행은 start() 메서드를 호출!

🚫 Thread 객체는 재사용 할 수 없습니다. ( → 하나의 객체는 한번만 start()가 가능합니다.)

🎈 익명 이너 클래스를 쓰레드 생성에 사용하기


익명 이너 클래스(Anonymous Inner Class)는 이름이 없는 내부 클래스로, 클래스를 정의하면서 동시에 인스턴스를 생성하는 방법입니다.
익명 이너 클래스를 사용하면 스레드에서 직접적으로 클래스를 정의하고 인스턴스를 생성할 수 있으므로, 코드의 간결성과 가독성을 높일 수 있습니다.
주로 간단한 작업이나 임시적인 스레드 생성에 활용됩니다.

        
        Thread thread = new Thread() {
            public void run() {
                // 스레드에서 실행할 코드 작성
            }
        };


        Runnable runnable = new Runnable() { 
        //인터페이스의 객체도 익명이너클래스를 사용하게되면 하위클래스를 직접 만들지 않아도 바로 생성이 가능함
        //new Runnable() { }의 중괄호 안에 미완성인 method run() 메서드를 overriding 하여 전부 기입하여 넘겨주었습니다.  
          public void run() {
                // 스레드에서 실행할 코드 작성
            }
        };

        Thread thread = new Thread(runnable);

 

🎈 Thread의 속성 ( Thread 객체 가져오기 / 이름 / Thread 개수 / 우선순위 / 데몬설정)


currentThread() 메서드 : Thread 클래스의 currentThread() 정적 static 메서드는 현재 실행 중인 쓰레드를 반환하는 정적 메서드입니다. 이 메서드를 호출하면 현재 실행 중인 쓰레드의 참조를 얻을 수 있습니다. 보통 currentThread() 메서드는 다중 쓰레드 환경에서 현재 실행 중인 쓰레드의 정보를 얻기 위해 사용됩니다. 이를 통해 각 쓰레드의 동작을 구분하거나, 특정 작업을 현재 쓰레드에서만 수행할 수 있도록 조건을 검사하는 등의 용도로 활용될 수 있습니다.
static Thread Thread.currentThread() → Thread 참조 객체가 없는 경우에 사용

 

        public class CurrentThreadExample {
            public static void main(String[] args) {
                Thread currentThread = Thread.currentThread(); // 현재 실행 중인 쓰레드 얻기
                System.out.println("현재 실행 중인 쓰레드: " + currentThread.getName());
            }
        }
728x90
반응형
728x90

 

💡 프로그램 vs 프로세스 vs 스레드의 차이 및 개념

 



프로그램(Program), 프로세스(Process), 스레드(Thread)는 컴퓨터에서 실행되는 작업 단위를 나타내는 용어입니다. 각각의 개념을 설명해보겠습니다:

  1. 프로그램(Program) : 프로그램은 실행 가능한 명령어들의 집합으로, 파일 시스템에 저장되어 있습니다.
    • 일반적으로 실행 파일 또는 실행 가능한 코드를 의미하며, 컴퓨터가 작업을 수행하는 데 필요한 지침을 포함합니다.
    • 예를 들어, 워드프로세서, 웹 브라우저, 게임 등은 각각 하나의 프로그램으로 볼 수 있습니다.

  2. 프로세스(Process) : 프로세스는 실행 중인 프로그램의 인스턴스로, 컴퓨터의 메모리에서 실행되고 있는 작업입니다.
    • 각각의 프로세스는 자체적인 메모리 공간, 자원 및 실행 상태를 가지고 있습니다.
    • 프로세스는 운영체제에 의해 생성되며, 각각 독립적으로 실행되어 다른 프로세스와는 분리되어 작동합니다.
    • 각 프로세스는 독립적인 실행 흐름을 가지며, 자신의 데이터와 자원에 접근할 수 있습니다.
    • 예를 들어, 워드프로세서가 실행 중일 때 해당 프로세스는 컴퓨터의 메모리에 로드되어 실행되고 있습니다.

  3. 쓰레드(Thread) : 스레드는 프로세스 내에서 실행되는 작은 작업 단위입니다.
    • 하나의 프로세스는 여러 개의 스레드를 가질 수 있으며, 이러한 스레드들은 동시에 실행될 수 있습니다.
    • 스레드는 프로세스 내의 메모리와 자원을 공유하면서 실행됩니다.
    • 여러 스레드가 동시에 작업을 수행하므로, 프로세스의 성능을 향상시키고 동시성을 제공합니다.
    • 예를 들어, 워드프로세서의 프로세스 내에서 스레드를 사용하여 동시에 문서를 저장하거나 인쇄할 수 있습니다.

요약하자면, 프로그램은 실행 가능한 명령어의 집합이고, 프로세스는 실행 중인 프로그램의 인스턴스입니다.
프로세스는 독립적으로 실행되는 작업을 의미하며, 각각 자체적인 메모리와 자원을 가지고 있습니다.
쓰레드는 프로세스 내에서 실행되는 작은 작업 단위로, 프로세스의 자원을 공유하면서 동시에 실행될 수 있습니다.
쓰레드를 사용하여 프로세스의 성능을 향상시키고 동시성을 제공할 수 있습니다.

쓰레드란 ?


쓰레드(thread)는 프로그램 내에서 동시에 실행되는 작업의 단위
입니다. 쉽게 말하면, 하나의 프로세스(프로그램) 안에서 동시에 여러 작업을 수행할 수 있게 해주는 것입니다. 쓰레드는 프로그램의 실행 흐름을 독립적으로 제어하며, 각각의 쓰레드는 별도의 실행 경로를 가지고 작업을 수행합니다.

쓰레드의 기본 개념을 이해하기 위해 다음과 같은 비유를 생각해볼 수 있습니다. 하나의 프로그램은 여러 쓰레드로 이루어진 "일꾼들의 작업공장"이라고 생각할 수 있습니다. 각각의 쓰레드는 자신이 맡은 작업을 동시에 처리하며, 각각 독립적인 실행 흐름을 가지고 작업을 수행합니다. 이렇게 작업공장에 여러 일꾼(쓰레드)이 있다면 작업을 훨씬 효율적으로 처리할 수 있습니다.

쓰레드를 사용하는 이유는 다양합니다. 주요한 이유는 다음과 같습니다:

  1. 동시성(Concurrency): 여러 작업을 동시에 실행하여 처리 속도를 향상시킬 수 있습니다. 예를 들어, 웹 브라우저에서 동시에 여러 웹 페이지를 로드하거나, 게임에서 동시에 여러 플레이어가 동작하는 등의 상황에서 쓰레드를 사용하여 동시성을 구현할 수 있습니다.

  2. 응답성(Responsiveness): 쓰레드를 사용하면 프로그램이 여러 작업을 동시에 처리할 수 있으므로, 일부 작업이 블록되거나 시간이 오래 걸리는 작업이 있더라도 다른 작업을 수행할 수 있습니다. 이를 통해 사용자 인터페이스의 응답성을 유지하고, 장기 실행 작업이 프로그램의 전반적인 동작을 방해하지 않도록 할 수 있습니다.
  3. 병렬성(Parallelism): 멀티코어 프로세서에서 쓰레드를 사용하여 여러 작업을 병렬로 실행할 수 있습니다. 이를 통해 작업을 효율적으로 분산하고, 작업의 처리 속도를 향상시킬 수 있습니다. / 각각의 코어가 각각의 작업을 진행하기 때문에 동시 수행이며, 처리 속도가 빨라집니다.

쓰레드를 사용하여 프로그램을 개발할 때는 쓰레드 간의 동기화와 상호작용, 공유 자원에 대한 접근 등 다양한 쓰레드 관련 이슈를 고려해야 합니다. 쓰레드를 잘 이해하고 적절하게 활용하면 다양한 유형의 작업을 효율적으로 처리할 수 있습니다.

💡
Q1. 작업이 3개, 코어가 4개인 경우 (동시성 / 병렬성)
Q2. 작업이 4개, 코어가 1개인 경우 (동시성 / 병렬성)
Q3. 작업이 6개, 코어가 2개인 경우 (동시성 / 병렬성)

Q1의 경우 작업에 비해 코어의 수가 많아 작업의 수량에 맞게 CPU코어를 분배해줄 수 있기 때문에 병렬성이 적합합니다.
Q2의 경우 작업에 비해 코어의 수가 적기 때문에 하나의 코어에서 여러 작업을 처리할 수 있는 동시성이 적합합니다.
Q3의 경우 각각의 작업이 동일하다면 작업에 비해 코어의 수가 적지만 1개 이상의 코어를 가지고 있기 때문에 작업을 3개씩 2개의 코어로 나누는 병렬성이 일어나고, 해당 코어 내에서 여러 작업을 처리할 수 있게 동시성이 수행하도록 합니다.

💡 Java Program 상의 Thread에 대해서 알아보자

java에서 main 메서드의 작업을 수행하는 것도 쓰레드 입니다. 이를 main쓰레드라고 하고, JVM이 main method를 실행하는데 JVM은 main method를 찾아서 프로그램을 실행해줍니다.
main method는 제일 먼저 main Thread를 생성(=최초의)합니다. 시작 시점에는 단일 Thread (main Thread) 1개만 존재합니다. 이 후 main Thread에서 Thread를 생성 / 실행하게 되면 Multi-Thread가 됩니다.

❓ Multi-Thread란 ?

Multi-thread(다중 스레드)는 하나의 프로세스 내에서 여러 개의 스레드가 동시에 실행되는 프로그래밍 모델을 의미합니다. 다중 스레드를 사용하면 여러 작업을 동시에 처리하고 병렬로 실행할 수 있어 성능과 응답성을 향상시킬 수 있습니다.

모든 프로세스에는 반드시 하나 이상의 thread가 존재하며 이를 단일 쓰레드라고 합니다. 단일 스레드에서는 하나의 작업을 순차적으로 실행하며, 다른 작업을 처리하기 위해서는 현재 작업이 완료될 때까지 기다려야 합니다.

728x90
반응형
728x90

 

 

💡내부 클래스(Inner Class)

내부 클래스는 다른 클래스 내에 선언된 클래스로, 외부 클래스의 멤버에 직접 접근할 수 있습니다.
클래스 안의 클래스 / 각각 따로 클래스를 만드는 것이 아닌 class 안에 class를 넣을 수 있습니다.내부 클래스안에서 외부 클래스의 멤버들에 쉽게 접근이 가능합니다.


🎈 Java에서는 변수의 선언위치에 따라 4종류의 내부 클래스를 사용할 수 있습니다
  1. 인스턴트 내부 클래스(Instance Inner Class) : 외부 클래스의 멤버변수 선언 위치에 선언하며, 외부 클래스의 인스턴스가 생성된 후에만 인스턴스 내부 클래스의 객체를 생성할 수 있습니다.
    주로 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용될 목적으로 선언됩니다. 인스턴스 내부 클래스는 외부 클래스의 멤버 변수와 메서드에 접근할 수 있습니다.
        **1. 인스턴스 내부 클래스 예제**


        **public class Outer** {  // outerMethod클래스와 innerMethod의 외부 클래스 
            private int x;


            public void outerMethod() { 
                // Outer (외부 클래스) 인스턴스 내부 클래스의 객체 생성
                Inner inner = new Inner();
                inner.innerMethod();
                    //outerMethod() 메서드 내에서 Inner 클래스의 객체를 생성하고, 
                    //이를 통해 innerMethod() 메서드를 호출하고 있습니다.
            }


            **public class Inner** { //Outer 클래스의 인스턴스 내부 클래스
                public void innerMethod() {
                    // 외부 클래스의 멤버에 접근
                    System.out.println(x);
                }
            }
        }

이 때, 내부 클래스인 Inner는 외부 클래스인 Outer를 제외하고는 다른 클래스에서 잘 사용되지 않는 것이어야 합니다.
내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있고, 코드의 복잡성을 줄일 수 있다는 장점이 있습니다.

  1. 정적 내부 클래스(Static Inner Class): 정적 내부 클래스는 외부 클래스의 인스턴스와 무관하게 독립적으로 생성될 수 있습니다.
    정적 내부 클래스는 외부 클래스의 정적 멤버에만 접근할 수 있습니다.
  2. 지역 내부 클래스(Local Inner Class): 메서드나 코드 블록 내에서 선언되는 클래스로, 해당 블록 내에서만 사용될 수 있습니다.
  3. 익명 내부 클래스(Anonymous Inner Class): 이름이 없는 클래스로, 인터페이스나 추상 클래스의 구현체를 정의하거나, 특정 클래스를 상속받아 재정의된 메서드를 가지는 객체를 생성할 때 사용됩니다.
    일회용으로 사용되는 클래스라고 보면 됩니다. GUI 프로그램에서 이벤트 처리 시 주로 사용됩니다.

 

변수가 선언된 위치에 따라 인스턴스 변수, 클래스(static)변수, 지역변수등으로 나뉘듯이 내부 클래스도 이와 마찬가지로 선언된 위치에 따라 달라집니다.
각 내부의 클래스 선원 이치에 따라 같은 선언위치의 변수와 동일한 유효범위(scope)와 접근성 (accessibility)을 갖게됩니다.

 

💡 내부 클래스의 제어자와 접근성

 

원래 class 앞에는 default(작성x), public 밖에 작성하지 못하는데, 내부 클래스에는 4가지의 제어자가 전부 사용가능합니다.

내부 클래스를 작성하는데, static 멤버가 필요하다면 내부클래스도 static으로 선언해주어야 합니다.
static 멤버라 함은 객체 생성없이 가능해야한는데, class 자체가 static이 아니라면 그 클래스는 인스턴스 클래스이기 때문에 객체 생성이 필요해지기 때문입니다.

        public class Ex_1_Inner {

            class InstanceInner {  //인스턴스 내부 클래스 
                int iv = 100;
                //static int cv = 100;
                final static int CONST = 100; 
                //다만 final과 static이 동시에 붙은 변수는 상수(constant) 이므로 모든 내부 클래스에서 정의가 가능하다.
            }

            static class StaticInner {   // static 내부 클래스
                int iv = 200;
                static int cv = 300;  //-> static class 만 static 멤버를 정의할 수 있다.
            }

            void myMethod() {  //인스턴스 메서드 (객체생성 필요)
                    class LocalInner {  //지역 내부 클래스
                        int iv = 300;
                        //static int cv = 300; -> static 변수 선언 불가능
                        final static int CONST = 300;
                    }
                    LocalInner in = new LocalInner();
                    System.out.println("외부클래스 안의 인스턴스 메서드 = "  + in.iv);
                    System.out.println("외부클래스 안의 인스턴스 메서드 = "  + in.CONST);

            }

            public static void main(String[] args) {
                System.out.println("내부 클래스 안의 멤버변수 = " + InstanceInner.CONST);
                System.out.println("내부 클래스 안의 멤버변수 = " + StaticInner.cv);

                //내부의 인스턴스 메서드를 사용하기 위해서 객체를 생성한 후 호출하였다.
                Ex_1_Inner ex1 = new Ex_1_Inner();
                ex1.myMethod();

            }
        }

        출력-> 내부 클래스 안의 멤버변수 = 100
                    내부 클래스 안의 멤버변수 = 300
                    외부클래스 안의 인스턴스 메서드 = 300
                    외부클래스 안의 인스턴스 메서드 = 300

 

InstanceInner 클래스의 CONST 변수는 final static변수로 선언되어 있어서, 클래스가 로드될 때 한 번 초기화되어 변경할 수 없는 상수입니다. 따라서 객체 생성 없이도 클래스 이름을 통해 직접 접근하여 사용할 수 있습니다. InstanceInner.CONST는 객체 생성과 무관하게 항상 같은 값을 갖는 상수이기 때문에 출력이 가능합니다.

 

💡 익명 이너 클래스(Anonymous Inner class)

 

다른 내부 클래스들과 달리 이름이 없고, 클래스의 선언과 객체의 생성을 동시에 하기때문에 단 한번만 사용될 수 있습니다. 오직 하나의 객체만을 생성할 수 있는 일회용 클래스입니다. 따라서, 생성자를 선언할 수도 없으며, 딱 한개의 클래스나 인터페이스를 상속받거나 구현할 수 있습니다.

        **// 익명 클래스는 선언과 동시에 생성하여 참조변수에 대입함.**

        **클래스이름 참조변수이름 = new 클래스이름(){

            // 메소드의 선언 (thread에서 자주 사용됨)

        };**

💡 예외 처리

프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료되는 경우가 있습니다. 이러한 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 합니다.

컴퓨터 시스템이 동작하는 도중에 예상하지 못한 상황이 발생하여 실행중인 프로그램이 영향을 받는 것을 에러(error)와 예외 (exception) 두가지로 구분할 수 있습니다.

  1. 에러 (error)는 프로그램 코드에서 의해서 수습될 수 없는 심각한 오류입니다. 시스템 레벨에서 프로그램에 심각한 문제를 발생시켜 실행 중인 프로그램을 종료시킵니다. 이러한 에러는 개발자가 미리 예측하여 처리할 수 없는 것이 대부분이므로, 오류에 대한 처리는 할 수 없습니다.
  2. 예외 (exception) 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류라고 하며, 에러와 마찬가지로 실행중인 프로그램을 비정상적으로 종료시키지만, 발생할 수 있는 상황을 미리 예측하여 처리할 수 있습니다. 따라서 개발자는 예외 처리(exception handling)를 통해서 예외 상황을 처리할 수 있도록 해야합니다. (대처 방안을 제시할 수 있는 error)
728x90
반응형
728x90
💡 Object 클래스란?


Java에서 Object는 모든 클래스의 최상위 부모 클래스입니다. 모든 클래스는 암묵적으로 Object 클래스를 상속받습니다.
Object 클래스는 Java의 기본적인 기능과 메서드를 제공하며, 모든 객체가 가져야 할 공통 동작을 정의합니다.

java에서 하나의 클래스를 만들면 기본적으로 생략된 코드들이 있다

① default packgae - ex) import.java.lang.*;
② 최상위 클래스 - ex) public class A extends Object ⇒ java.lang.Object
③ default 생성자 - ex) public A() { super(); }

 

https://yeomboyeon.tistory.com/67

 

💡 Object 클래스내의 자주 쓰이는 메서드

 

Object 클래스의 주요 메서드로는 다음과 같은 것들이 있습니다:

  1. equals(): 객체의 동등성을 비교하는 메서드입니다. 기본적으로는 객체의 주소 비교를 수행하지만, 필요에 따라 재정의하여 객체의 내용 비교를 수행할 수 있습니다.
  2. hashCode(): 객체의 해시 코드 값을 반환하는 메서드입니다. 객체의 동등성을 검사하는데 사용될 수 있습니다.
  3. toString(): 객체를 문자열로 표현하는 메서드입니다. 기본적으로는 클래스명과 해시 코드를 반환하며, 필요에 따라 재정의하여 객체의 정보를 적절히 표현할 수 있습니다.
  4. getClass(): 객체의 클래스 정보를 반환하는 메서드입니다. Class 클래스의 인스턴스를 반환합니다.
  5. clone(): 객체를 복제하는 메서드입니다. 객체의 얕은 복사를 수행합니다.
  6. finalize(): 객체가 소멸될 때 호출되는 메서드입니다. Java에서는 가비지 컬렉터가 객체를 수거하기 전에 이 메서드를 호출합니다.

Object 클래스의 존재는 모든 객체가 공통적인 기능과 동작을 가지도록 하여 객체 지향 프로그래밍의 기본 원칙을 준수할 수 있도록 도와줍니다. 또한, 다형성을 지원하고 객체를 일반적인 형식으로 다룰 수 있도록 합니다.

 

        //B클래스 

        public class B extends Object {
            public void printGo() {
                System.out.println("나는 B입니다.");
            }
        }


        //A클래스 
        public class A extends Object{ //생략된 기본 최상위클래스 Object와 상속관계

            public A(){  //생략된 A클래스의 default 생성자
                super();
            }

            public void display() {
                System.out.println("나는 A 클래스 입니다.");
            }
            public void printGo() {
                System.out.println("나는 A 클래스 입니다.");
            }

        }

        //main 메서드 클래스
        import Poly.A;
        import Poly.B;

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

                A a = new A();
                display(a);  // display 메서드에 A 객체 전달
                B b = new B();
                display(b);  // display 메서드에 B 객체 전달

            }

            private static void display(A a) {
                a.printGo();
            }
            private static void display(B b) {
                b.printGo();
            }
        }

 

display() 메서드에 인수로 A a를 전달하는 이유는 메서드 내부에서 A 클래스에 정의된 printGo() 메서드를 호출하기 위해서입니다.

만약 display() 메서드에 인수로 a만 전달하면, 컴파일러는 해당 인수의 타입을 알 수 없기 때문에 어떤 메서드를 호출해야 할지 판단할 수 없습니다. 따라서 인수에 타입을 명시하여 컴파일러에게 전달된 객체의 타입을 알려주어야 합니다.

이렇게 타입을 명시함으로써 컴파일러는 A a가 A 클래스의 인스턴스임을 알 수 있고, 따라서 A 클래스에 정의된 메서드인 printGo()를 호출할 수 있게 됩니다. 타입의 명시는 컴파일러가 올바른 메서드 호출을 검증하고 실행 가능한 코드를 생성하는 데 도움을 줍니다.

728x90
반응형
728x90
Q. 서로 다른 동작을 가지는 클래스를 상속관계로 만들어서 동작을 시켜야한다고 가정해보자
서로 다른 동작의 클래스도 공통 기능을 만들어서 상속구조로 사용 가능?


https://velog.io/@mmy789/

 

상위클래스가 일반클래스인 클래스와의 상속관계에서는 하위클래스에서 상위클래스로 객체를 생성해서 사용할 때, 각각의 클래스들의 담겨져 있는 메서드를 사용할때 재정의(Override)를 해야하는 것은 아닙니다 ⇒ 다형성이 보장되진 않습니다.

상위클래스가 추상클래스인 클래스와의 상속관계에서는 추상클래스 내에 생성된 추상메서드에 따라 하위클래스에서 꼭 재정의(Override)를 해야합니다. 재정의를 하게 만들어서 다형성을 보장할 수는 있지만, 상위클래스는 구현클래스를 가질 수 있어서 이로 인해 하위클래스가 오동작할 수 있습니다.
재정의를 하게 만들어서 다형성을 보장할 수는 있지만, 상위클래스는 구현클래스를 가질 수 있어서 이로 인해 하위클래스가 오동작할 수 있습니다. ⇒ 추상메서드와 일반구현메서드가 서로 다른 동작을 가지는 클래스이기 때문

이렇게 서로 다른 동작(=메서드)을 가지는 클래스를 상속관계로 만들어서 동작을 시켜야 할 때에는 클래스가 아닌 인터페이스를 선언하여 사용해야 합니다.

 

❓ 인터페이스란 ?

객체 지향 프로그래밍에서 클래스와 관련된 동작(메서드)의 집합을 정의하는 추상화된 형식이며, 클래스에서 구현해야 하는 메서드들의 목록을 정의하는 역할을 합니다. 다른 클래스들이 특정 동작을 구현하도록 규약을 제공하며, 클래스 간의 계약(contract)을 정의하는 역할을 합니다.

* interface 문법
  interface 인터페이스 이름  {
        public static final 타입 상수이름 = 값;
        public abstract 메서드이름(매개변수 목록);
  }

 

인터페이스도 일종의 추상클래스라고 볼 수 있지만, 추상클래스와 달리 추상 메서드만을 포함하며, 일반 메서드, 멤버 변수, 생성자를 가질 수 없습니다. 클래스에서 인터페이스를 구현할 때는 모든 추상 메서드를 반드시 구현해야 합니다.

인터페이스는 interface 키워드를 사용하여 정의되며, 메서드의 시그니처(이름, 매개변수, 반환값)만을 선언하고, 구현 내용을 가지지 않습니다. 또한, 추상메서드와 final static 상수만 가질 수 있습니다. 클래스는 인터페이스를 implements 키워드를 사용하여 구현할 수 있으며, 인터페이스에 정의된 모든 메서드를 반드시 구현해야 합니다.

 

인터페이스는 구현메서드가 없기 때문에 객체 생성이 불가능하지만 Upcasting으로 상위클래스의 역할은 할 수 있습니다.인터페이스는 다음과 같은 특징을 갖습니다:

  1. 추상 메서드: 인터페이스는 추상 메서드만을 가지므로, 메서드의 구현 내용이 없습니다. 따라서, 인터페이스를 구현하는 클래스에서는 인터페이스에 정의된 메서드를 반드시 구현해야 합니다.
  2. 다중 상속: 클래스는 하나의 클래스만 상속할 수 있지만, 인터페이스는 여러 개의 인터페이스를 동시에 구현할 수 있습니다. 이를 통해 다중 상속의 일부 기능을 구현할 수 있습니다.
  3. 계약: 인터페이스는 클래스들 사이의 계약(Contract)을 정의하는 역할을 합니다. 클래스가 인터페이스를 구현한다는 것은 인터페이스에 정의된 메서드를 반드시 구현한다는 의미이며, 이를 통해 클래스들 간의 일관성을 유지할 수 있습니다.
  4. 다형성: 인터페이스를 사용하면 여러 클래스들을 하나의 인터페이스 타입으로 참조할 수 있으며, 이를 통해 다형성의 특징을 활용할 수 있습니다.
728x90
반응형
728x90
❓ 다형성(Polymorphism) 이란 ?

 

동일한 메시지(메서드 호출)에 대해 서로 다른 객체가 다르게 동작하는 능력(message pholymorphism)을 말합니다.
다형성은 한 가지 타입 또는 인터페이스를 여러 개의 구현체가 가질 수 있도록 함으로써 유연하고 확장 가능한 코드를 작성할 수 있게 합니다.

다형성은 상속과 관련하여 사용되며, 부모 클래스와 그 자식 클래스들 간에 동일한 메서드 이름을 가지고 있지만 각각의 클래스에서 다른 구현을 가지도록 하는 것을 의미합니다.
다형성은 같은 타입으로 선언된 객체가 실제로 실행 시에는 여러 다른 타입의 객체로 동작할 수 있게 합니다.

다형성은 다음과 같은 방식으로 구현됩니다:

  1. 업캐스팅(Upcasting): 자식 클래스의 인스턴스를 부모 클래스의 참조 변수로 참조하는 것을 말합니다. 부모 클래스의 참조 변수를 통해 자식 클래스의 객체에 접근할 수 있습니다. 업캐스팅을 통해 여러 개의 자식 클래스를 하나의 부모 클래스 타입으로 관리할 수 있습니다.
  2. 메서드 오버라이딩(Method Overriding): 자식 클래스에서 부모 클래스의 메서드를 재정의하여 자식 클래스에서 특화된 구현을 제공합니다. 부모 클래스의 메서드를 자식 클래스에서 동일한 이름으로 재정의하면, 실행 시에는 실제 객체의 타입에 맞는 메서드가 호출됩니다.
  3. 다형적 변수(Polymorphic Variables): 부모 클래스의 참조 변수를 사용하여 여러 자식 클래스의 객체를 참조할 수 있습니다. 이렇게 다형적 변수를 사용하면 실행 시에 실제 객체의 타입에 따라 다른 동작을 수행할 수 있습니다.

다형성은 코드의 유연성과 재사용성을 높여주며, 객체 지향 프로그래밍의 핵심 원칙 중 하나인 "프로그램을 작성할 때는 구체적인 타입보다는 추상적인 타입에 의존하라(Depend upon abstractions)"를 실현하는 방법 중 하나입니다.

 

https://kadosholy.tistory.com/99

 

 

        //Cat 클래스 (자식)
        public class Cat extends Animal {
                public void eat() {
                    System.out.println("고양이처럼 밥 먹는다");
                }
        }

        //Dog 클래스 (자식)
        public class Dog extends Animal{

            public void eat() {
                System.out.println("강아지처럼 먹어보자");
            }
        }

        //Animal 클래스 (부모)
        public class Animal {
            public void eat() {
                System.out.println("동물처럼 먹다");
            }
        }

        //Override 클래스 (메인)
        public static void main(String[] args) {
        /*
        Upcastiong(업캐스팅) -> 만약 다른 사람이 Dog클래스를 java 소스코드를 주지 않고, 
        class 파일만 주었을 때, Dog 클래스로 선언하기 어려움

        Animal <---> Dog.class(o) Animal 클래스가 Dog클래스와 상속관계에 있다는 것을 
        알고있다는 가정하에 Override 통해 eat()을 재 정의 할 수 있게 됩니다.
        */
                Animal ani = new Dog();
                ani.eat();
        // Animal ----실행시점에서 메서드가 결정되는 것(동적바인딩)---> Dog
        // 작성 시에는 eat()가 Animal에 속해있는 것이라고 생각되어 에러가 나지 않았지만, 실행과 동시에 Dog의 eat()이 실행되게 됩니다 / 이를 동적바인딩이라고 합니다.
                System.out.println("ani = " + ani);
            }
        }

앞서 업캐스팅과 동적바인딩을 설명할 때 , 사용했던 코드로 다형성을 설명해보면, 부모클래스 (Animal)이 먹다(eat())
메서드를 자식클래스인 Dog와 Cat에게 메세지를 보내면 자식 클래스가 반응을 하는데, 두 자식 클래스에서 eat() 메서드가 개와 고양이로 다르기 때문에 서로 다르게 반응을 하게 됩니다.

다형성을 적용하기 위해서는 Upcasting으로 객체를 생성 상속 관계로 되어 있어야 하며, 재 정의가 되어있고, 메모리에서 동적 바인딩이 되어있는 관계여야 가능하다.
상위클래스가 동일한 메세지로 하위 클래스를 서로 다르게 동작시키는 객체지향 원리 (=message polymorphism)

컴파일 시점에선 eat()가 Animal내의 eat() , 실행시점에서 호출될 메서드가 결정되는 바인딩 = 동적바인딩
-> 동적바인딩이 일어나야 eat()라는 메서드가 Animal것인지 Dog것인지 알 수 있다.

 

💡 다형성(message polymorphism)의 전제조건

1. 상속관계    2. Override(재정의)    3. Upcasting(업캐스팅)   4. 동적 바인딩

 

💡 다형성 활용 - 다형성 인수란 ?

다형성 인수(Polymorphic Parameters)란, 메서드나 함수의 매개변수(parameter)를 다형성(polymorphism)을 활용하여 여러 타입의 객체를 전달할 수 있는 것을 말합니다. 이를 통해 코드의 재사용성과 유연성을 높일 수 있습니다.

 

        public void makeShape(Shape shape) {
            // 도형을 처리하는 로직
            shape.draw();
        }

        Circle circle = new Circle();
        Triangle triangle = new Triangle();
        Rectangle rectangle = new Rectangle();

        makeShape(circle);     // Circle 객체 전달
        makeShape(triangle);   // Triangle 객체 전달
        makeShape(rectangle);  // Rectangle 객체 전달

 

위의 코드에서 makeShape 메서드는 Shape 타입의 매개변수 shape를 받습니다. 이 매개변수는 다형성 인수로, Shape 클래스를 상속한 여러 도형 클래스의 인스턴스를 전달할 수 있습니다. 예를 들면, Circle, Triangle, Rectangle 클래스가 Shape 클래스를 상속한다고 가정해보겠습니다.

위의 코드에서 makeShape 메서드에 각각의 도형 객체를 전달하고 있습니다. 이 때, 전달되는 객체는 각자의 실제 타입인 Circle, Triangle, Rectangle 이지만, 메서드 내부에서는 Shape 타입으로 참조되고 처리됩니다. 이는 다형성의 개념을 활용한 예시로서, 동일한 메서드를 여러 타입의 객체에 대해 재사용할 수 있음을 보여줍니다.

다형성 인수는 코드의 유연성과 확장성을 제공하며, 객체지향 프로그래밍에서 중요한 개념 중 하나입니다.

instanceof 연산자 : instanceof 연산자는 객체의 타입을 확인하기 위해 사용되는 연산자 사용 문법
[객체] instanceof [타입]

instanceof를 사용하여 특정 객체가 특정 클래스의 인스턴스인지를 확인할 수 있습니다.

        Shape shape = new Circle();

        if (shape instanceof Circle) {
            System.out.println("shape은 Circle의 인스턴스입니다.");
        } else if (shape instanceof Rectangle) {
            System.out.println("shape은 Rectangle의 인스턴스입니다.");
        } else {
            System.out.println("shape은 Circle 또는 Rectangle의 인스턴스가 아닙니다.");
        }


위의 코드에서 makeShape 메서드는 Shape 타입의 매개변수 shape를 받고, shape 객체의 타입을 instanceof 연산자를 사용하여 확인합니다. 이후 해당하는 타입으로 캐스팅하여 각각의 타입에 맞는 처리 로직을 수행합니다. instanceof 연산자는 주어진 객체가 해당 타입의 인스턴스인지를 불리언(Boolean) 값으로 반환합니다. 만약 객체가 해당 타입의 인스턴스이면 true를 반환하고, 그렇지 않으면 false를 반환합니다.

instanceof 연산자는 객체의 상속 관계나 인터페이스 구현 여부를 확인할 때 주로 사용됩니다. 타입 검사를 통해 객체의 동적인 타입을 파악하여 적절한 동작을 수행할 수 있습니다. 다형성과 함께 사용하면 유연하고 확장 가능한 코드를 작성할 수 있습니다.

💡 다형성 활용 - 다형성 배열(상위타입 배열)이란?

 

다형성배열이란 ? 다형성 개념을 배열에 적용한 것을 의미합니다. 성 배열은 여러 타입(서로 다른 타입)의 객체를 하나의 배열에 저장할 수 있는 특징을 갖고 있습니다.

일반적으로 배열은 동일한 타입의 객체들만 저장할 수 있지만, 다형성 배열은 부모 클래스를 상속받은 서브 클래스들을 모두 저장할 수 있습니다. 이를 통해 서로 다른 타입의 객체를 하나의 배열에 함께 저장하고 관리할 수 있습니다. 부모클래스 타입의 배열은 자식클래스 타입을 저장하는 것이 가능합니다.

728x90
반응형

+ Recent posts