7 분 소요


1. 개요

1.1 객체 지향 프로그래밍(OOP) 개념

  • 객체 지향 프로그래밍(Object-Oriented Programming, OOP)은 소프트웨어 설계와 프로그래밍 패러다임 중 하나로, 프로그램을 객체라는 기본 단위로 구성하는 방식을 의미한다.
  • 객체는 데이터와 해당 데이터를 처리하는 메서드(함수)를 포함하며, 이 두 가지를 하나의 단위로 묶어 캡슐화(encapsulation)한다.


1.2 OOP 특징

개념 설명
클래스(Class) 객체를 정의하는 틀 혹은 설계도이다. 클래스는 객체의 속성과 행동을 정의한다.
객체(Object) 클래스에 의해 생성된 인스턴스이다. 객체는 클래스에서 정의된 속성과 행동을 가진다.
상속(Inheritance) 한 클래스가 다른 클래스의 속성과 메서드를 물려받는 기능이다. 이를 통해 코드의 재사용성을 높일 수 있다.
다형성(Polymorphism) 동일한 메서드나 연산자가 다양한 방식으로 동작할 수 있게 하는 기능이다. 이는 주로 오버로딩과 오버라이딩을 통해 구현된다.(동일한 이름의 메서드가 상황에 따라 다른 일을 할 수 있음)
캡슐화(Encapsulation) 객체의 속성과 메서드를 하나로 묶고, 외부에서 접근을 제한하여 객체의 내부 상태를 보호하는 메커니즘이다.
추상화(Abstraction) 복잡한 시스템을 단순화하여 중요한 부분만을 모델링하는 과정이다. 이를 통해 복잡성을 줄이고 코드의 이해와 유지보수를 쉽게 한다.
  • 즉, 객체 지향 프로그래밍은 코드의 재사용성, 유지보수성, 확장성을 높이는 데 유리하며, 복잡한 소프트웨어 시스템을 관리하기 쉽게 한다.
  • 대표적인 객체 지향 프로그래밍 언어로는 자바(Java), C++, 파이썬(Python), C# 등이 있다.



2. 클래스와 인스턴스

2.1 클래스, 객체, 인스턴스

위 용어를 붕어빵 틀에 비유해보자.

개념 비유 설명
클래스(Class) 붕어빵 틀 붕어빵의 모양, 크기, 재료 등을 정의하는 설계도. 객체의 속성(크기, 모양, 재료)과 행동(굽는 방법)을 정의.
객체(Object) 붕어빵 붕어빵 틀로 만든 실제 붕어빵. 같은 틀에서 나왔지만 재료(팥, 슈크림 등)에 따라 다를 수 있음.
인스턴스(Instance) 특정한 붕어빵 붕어빵 틀로 만들어진 구체적인 붕어빵 하나. 예: 팥이 들어간 붕어빵, 슈크림이 들어간 붕어빵.
  • 즉, 객체를 만들 때마다 새로 정의하는 것이 아니고, 기존에 객체의 형태를 미리 정의해 놓은 클래스를 사용하는 것이다.
  • 클래스에서 객체가 생성되는 것을 실체화 라고 하며, 구체적으로 생성된 객체를 인스턴스라고 한다.(일반적으로 인스턴스와 객체를 혼용하기도 함)


2.2 클래스 생성

  • 패키지 이름: projectOOP
  • 클래스 이름: _01_othersOOP

클래스 직접 사용

Math는 클래스이고, 클래스 안에는 변수(PI 등), 메서드(floor, ceil 등)이 존재한다.

package projectOOP;

public class _01_othersOOP {
    public static void main(String[] args) {
        System.out.println(Math.PI);
        System.out.println(Math.floor(1.8));
        System.out.println(Math.ceil(1.8));
    }
}


클래스 복제 사용

FileWriter 클래스를 사용하여 파일을 생성할 수 있다.

package projectOOP;

import java.io.FileWriter;
import java.io.IOException;

public class _01_othersOOP {
    public static void main(String[] args) throws IOException {
        FileWriter f1= new FileWriter("data.text");
        f1.write("Hello");
        f1.write("Java");

        f1.close();
    }
}


즉, 클래스와 인스턴스를 정리하면 다음과 같다.

  • 클래스: System, Math, FileWriter
  • 인스턴스: f1



3. 변수와 메소드

3.1 메소드 생성하기

printA, printB라는 메소드를 생성해보자

package projectOOP;

public class _02_myOOP {
    public static String delimiter="";

    public static void main(String[] args) {
        delimiter="---";
        printA();
        printB();

        delimiter="***";
        printA();
        printB();
    }
    public static void printA(){
        System.out.println(delimiter);
        System.out.println("A");
    }

    public static void printB() {
        System.out.println(delimiter);
        System.out.println("B");
    }
}


프로그램 실행 흐름

  1. delimiter를 “—“로 설정
  2. printA()를 호출하여 delimiter와 “A”를 출력
  3. printB()를 호출하여 delimiter와 “B”를 출력
  4. delimiter를 “***“로 변경
  5. printA()를 호출하여 새로운 delimiter와 “A”를 출력
  6. printB()를 호출하여 새로운 delimiter와 “B”를 출력



4. 클래스 사용하기

delimiter와 printA, printB 메소드가 서로 연관되어 있으므로, 클래스를 통해 이를 하나의 단위로 묶어서 관리해보자

출력 결과는 동일하다.

package projectOOP;

// Print 클래스 생성
class Print{
    public static String delimiter="";
    public static void A(){
        System.out.println(delimiter);
        System.out.println("A");
    }

    public static void B() {
        System.out.println(delimiter);
        System.out.println("B");
    }

}

public class _02_myOOP {
    public static void main(String[] args) {
        Print.delimiter="---";
        Print.A(); // Print라는 클래스에 소속되어있는 A메소드
        Print.B();

        Print.delimiter="***";
        Print.A();
        Print.B();
    }

}
  • 이렇게 Print라는 클래스가 출력과 관련된 작업을 수행한다는 것을 쉽게 알 수 있다! -> 가독성 ↑
  • 클래스안에는 변수와 메소드들이 들어있는데, 이를 멤버라고한다. (위 코드에서 멤버는 delimiter, A, B)



5. 인스턴스

위 코드를 인스턴스를 생성하여 효율적으로 만들어보자. 출력 결과는 위와 동일하다

package projectOOP;
class Print {
    //  Instance를 만들어내기 위해 "static"을 지워야 함
    public String delimiter="";

    public void A(){
        System.out.println(delimiter);
        System.out.println("A");
    }
    public void B(){
        System.out.println(delimiter);
        System.out.println("B");
    }
}

public class _01_othersOOP {
    public static void main(String[] args) {
        Print p1 = new Print();
        p1.delimiter="---";

        Print p2 = new Print();
        p2.delimiter="***";

        p1.A();
        p1.B();
        p2.A();
        p2.B();
    }
}
  • 이처럼 Print라는 클래스 복제한 인스턴스를 통해 한 번 정의한 클래스를 여러 곳에서 재사용할 수 있게 함으로써 코드의 간결성과 코드의 중복을 제거할 수 있다.


6. static

static이 있는건 클래스 소속(클래스 이름을 통해 직접 접근하거나 호출 가능), 없는 것은 인스턴스 소속(인스턴스를 통해서만 접근하거나 호출 가능)이다.

package projectOOP;

class Foo {

    // 1) 정적 변수
    public static String classVar = "I class var";

    // 2) 인스턴스 변수
    public String instanceVar = "I instance var";

    // 3) 정적 메서드
    public static void classMethod() {
        System.out.println(classVar); // Ok: 정적 메서드에서 정적 변수에 접근 가능
        // System.out.println(instanceVar); // Error: 정적 메서드에서는 인스턴스 변수에 접근 불가
    }

    // 4) 인스턴스 메서드
    public void instanceMethod() {
        System.out.println(classVar); // Ok: 인스턴스 메서드에서 정적 변수에 접근 가능
        System.out.println(instanceVar); // Ok: 인스턴스 메서드에서 인스턴스 변수에 접근 가능
    }
}

public class _02_StaticApp {
    public static void main(String[] args) {
        /*** 클래스 변수 -> 클래스 이름을 통해 호출 가능 ***/
        System.out.println(Foo.classVar); // Ok: 클래스 변수는 클래스 이름을 통해 접근 가능

        /*** 인스턴스 변수 -> 인스턴스를 통해서만 접근 가능 ***/
        // System.out.println(Foo.instanceVar); // Error: 인스턴스 변수는 인스턴스를 통해서만 접근 가능

        /*** 클래스 메서드 -> 클래스 이름을 통해 호출 가능 ***/
        Foo.classMethod(); // Ok: 클래스 메서드는 클래스 이름을 통해 호출 가능

        /*** 인스턴스 메서드 -> 인스턴스를 통해서만 호출 가능 ***/
        // Foo.instanceMethod(); // Error: 인스턴스 메서드는 인스턴스를 통해서만 호출 가능

        // 인스턴스 생성
        Foo f1 = new Foo();
        Foo f2 = new Foo();

        /*** 인스턴스 변수를 통해 접근 ***/
        System.out.println(f1.classVar); // I class var: 인스턴스를 통해 정적 변수에 접근 가능하지만 권장되지 않음
        System.out.println(f1.instanceVar); // I instance var: 인스턴스를 통해 인스턴스 변수에 접근 가능

        /*** 정적 변수 변경 ***/
        f1.classVar = "changed by f1";
        System.out.println(Foo.classVar); // changed by f1: 정적 변수는 모든 인스턴스에 공유됨
        System.out.println(f2.classVar); // changed by f1: 정적 변수는 모든 인스턴스에 공유됨

        /*** 정적 변수 다시 변경 ***/
        f2.classVar = "changed by f2";
        System.out.println(Foo.classVar); // changed by f2: 정적 변수는 모든 인스턴스에 공유됨
        System.out.println(f2.instanceVar); // I instance var: 인스턴스 변수는 각 인스턴스별로 독립적
    }
}



7. 생성자와 this

  • 생성자(Constructor)는 인스턴스가 생성될 때 호출되는 인스턴스 초기화 메서드로, 객체 초기화를 위해 사용된다.
  • 생성자는 반환 타입이 없고, 직접 호출되지 않으며 객체 생성 시 new 키워드를 통해 호출된다.
  • 생성자의 이름은 클래스의 이름과 동일해야 한다.
package projectOOP;
class Print {
    // 생성자
    public Print(String _delimiter){
        delimiter=_delimiter;
    }
    public String delimiter="";

    public void A(){
        System.out.println(delimiter);
        System.out.println("A");
    }
    public void B(){
        System.out.println(delimiter);
        System.out.println("B");
    }
}

public class _01_othersOOP {
    public static void main(String[] args) {
        Print p1 = new Print("---");
        Print p2 = new Print("***");

        p1.A();
        p1.B();
        p2.A();
        p2.B();
    }
}


  • 생성자나 메소드 내에서 인스턴스 변수와 로컬 변수의 이름이 동일할 때, this를 사용하여 인스턴스 변수를 명확히 구분할 수 있다.
  • 아래 코드에서 this.delimiter는 객체의 인스턴스 변수이고, delimiter는 생성자의 매개변수이다.
  • 즉, this는 인스턴스 자기 자신을 의미하며 객체 자신을 참조하기 위해 사용된다.
package projectOOP;
class Print {
    // 생성자
    public String delimiter="";

    public Print(String delimiter){
        this.delimiter = delimiter;
    }

    public void A(){
        System.out.println(this.delimiter);
        System.out.println("A");
    }
    public void B(){
        System.out.println(this.delimiter);
        System.out.println("B");
    }
}

public class _01_othersOOP {
    public static void main(String[] args) {
        Print p1 = new Print("---");
        Print p2 = new Print("***");

        p1.A();
        p1.B();
        p2.A();
        p2.B();
    }
}


8. 활용

8.1 클래스화

아래 코드를 클래스화 시켜보자

package projectOOP;
public class AccountingApp {
    // 공급가액
    public static double valueOfSupply = 10000.0;
    // 부가가치세율
    public static double vatRate = 0.1;

    public static double getVAT() {
        return valueOfSupply * vatRate;
    }

    public static double getTotal() {
        return valueOfSupply + getVAT();
    }

    public static void main(String[] args) {
        System.out.println("Value of supply : " + valueOfSupply);
        System.out.println("VAT : " + getVAT());
        System.out.println("Total : " + getTotal());
    }

}
class Accounting{
    public static double valueOfSupply;
    public static double vatRate = 0.1;
    public static double getVAT() {
        return valueOfSupply * vatRate;
    }
    public static double getTotal() {
        return valueOfSupply + getVAT();
    }
}
public class AccountingApp {
    public static void main(String[] args) {
        Accounting.valueOfSupply = 10000.0;
        System.out.println("Value of supply : " + Accounting.valueOfSupply);
        System.out.println("VAT : " + Accounting.getVAT());
        System.out.println("Total : " + Accounting.getTotal());

    }
}
package projectOOP;

// 부가가치세와 관련된 정보를 모아둔 클래스
class Accounting{
    // 부가가치세율
    public static double valueOfSupply;
    public static double vatRate = 0.1;
    public static double getVAT() {
        return valueOfSupply * vatRate;
    }
    public static double getTotal() {
        return valueOfSupply + getVAT();
    }

}
public class AccountingApp {
    public static void main(String[] args) {
        // 공급가액
        Accounting.valueOfSupply = 10000.0;

        System.out.println("Value of supply : " + Accounting.valueOfSupply);
        System.out.println("VAT : " + Accounting.getVAT());
        System.out.println("Total : " + Accounting.getTotal());

    }
}


8.2 인스턴스화

공급가액, 부가가치세 등 값이 변경되면 어떻게해야할까?

package projectOOP;

// 부가가치세와 관련된 정보를 모아둔 클래스
class Accounting{
    // 부가가치세율
    // static 제거
    public double valueOfSupply;
    public static double vatRate = 0.1;
    public double getVAT() {
        return valueOfSupply * vatRate;
    }
    public double getTotal() {
        return valueOfSupply + getVAT();
    }

}
public class AccountingApp {
    public static void main(String[] args) {
        Accounting a1 = new Accounting();
        a1.valueOfSupply = 10000.0;

        Accounting a2 = new Accounting();
        a2.valueOfSupply = 20000.0;

        System.out.println("Value of supply : " + a1.valueOfSupply); // 인스턴스에 소속된 변수이므로 static 제거해야함
        System.out.println("Value of supply : " + a2.valueOfSupply);
    }

}


태그:

카테고리:

업데이트:

댓글남기기