[Java] 객체 지향 프로그래밍, Java

클래스, 메서드, 인스턴스 등의 기본 개념과 활용 예시를 다루며, 각각의 용어가 어떻게 코드 내에서 작동하는지를 이해하는 데 도움을 준다.
이를 통해 절차 지향 프로그래밍에서 객체 지향 프로그래밍으로의 전환이나 코드의 가독성 향상, 중복 선언을 피하는 방법 등에 대해 배울 수 있다.
또한, 클래스와 인스턴스의 차이, static의 사용, 생성자와 this의 활용 등에 대한 설명도 포함되어 있다.

 

 

 

OOP

객체 지향 프로그래밍(OOP, Object Oriented Programming)은 클래스를 중심으로 프로그램의 구조를 만들어나가는 컴퓨터 프로그래밍 방법론을 의미한다.

이런 방법론을 언어 차원에서 제공하는 언어를 객체 지향 언어라고 한다.

또, 메서드를 다른 컴퓨터 언어에서는 function이나 subroutine, procedural이라고 부른다.

  • 절차 지향 프로그래밍 (Procedural Programming)
    메서드를 이용해 프로그램을 정리 정돈하는 기법

클래스 및 인스턴스

클래스비슷한 맥락의 변수나 메서드들을 묶어 그룹핑한 것이며, 인스턴스클래스를 복제하여 복제한 클래스(인스턴스)에 대해 길고 복잡한 작업을 할 때 활용할 수 있다.

  • 일회성 작업
    메서드를 변수나 클래스에 소속된 그대로 사용
  • 긴 맥락의 작업
    클래스를 직접 사용하는 것이 아닌 클래스를 복제해 제어

 

아래는 System.out.println()이라는 코드를 예시로 설명한 내용이다.

  • System
    클래스
  • out
    static 변수 (printStream 클래스의 객체)
  • println
    메서드 (System 클래스 안에 존재하는 Inner 클래스인 printStream 클래스의 메서드)
import java.io.FileWriter;
import java.io.IOException;

public class OthersOOP {
    public static void main(String[] args) throws IOException {
        // Math 클래스를 바로 사용 (일회성)
        System.out.println(Math.PI);
        System.out.println(Math.floor(1.8));
        System.out.println(Math.ceil(1.8));
        
        // f1이라는 변수에 담기는 복제본 생성 (긴 맥락의 작업)
        FileWriter f1 = new FileWriter("data.txt");
        f1.write("Hello");
        f1.write(" Java");
        f1.close();
        
        FileWriter f2 = new FileWriter("data2.txt");
        f2.write("Hello");
        f2.write(" Java2");
        f2.close();
    }
}

 

클래스

  • public 클래스 매개변수

클래스는 연관된 메서드와 변수를 묶어 하나의 이름을 갖는 객체로 만든다.
메서드와 변수 앞에 클래스 이름을 붙여 이름만으로도 내용을 알 수 있도록 가독성을 향상시킨다.

class Print{
    public static String delimiter = "";
    public static void A() {
        System.out.println(delimiter);
        System.out.println("A");
        System.out.println("A");
    }
    
    public static void B() {
        System.out.println(delimiter);
        System.out.println("B");
        System.out.println("B");
    }
}

public class MyOOP {
    public static void main(String[] args) {
        Print.delimiter = "----";
        Print.A();
        Print.A();
        Print.B();
        Print.B();
        
        Print.delimiter = "****";
        Print.A();
        Print.A();
        Print.B();
        Print.B();
    }
}Copy to clipboardErrorCopied

 

메인 클래스와 함께 내장된 클래스를 외부로 독립시켜 다른 앱에서도 사용할 수 있도록 한다.

// MyOOP.java
public class MyOOP {
    public static void main(String[] args) {
        Print.delimiter = "----";
        Print.A();
        Print.A();
        Print.B();
        Print.B();
        
        Print.delimiter = "****";
        Print.A();
        Print.A();
        Print.B();
        Print.B();
    }
}Copy to clipboardErrorCopied
// Print.java
class Print{
    public static String delimiter = "";
    public static void A() {
        System.out.println(delimiter);
        System.out.println("A");
        System.out.println("A");
    }
    
    public static void B() {
        System.out.println(delimiter);
        System.out.println("B");
        System.out.println("B");
    }
}Copy to clipboardErrorCopied

 

연관된 메서드를 클래스로 정리한다. (손쉬운 코드의 목적 파악)

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());
    }
}

 

인스턴스

클래스를 복제한 것을 인스턴스라고 한다.

하나의 클래스의 상태가 계속해 바뀌는 복잡한 코드일 경우 클래스를 복제한 인스턴스를 활용해 각각의 인스턴스에 값을 넣고 필요한 값에 따라 필요한 인스턴스의 메서드를 호출 시 깔끔한 코드 정리가 가능해진다.

public class MyOOP {
    public static void main(String[] args) {
        Print p1 = new Print();
        p1.delimiter = "----";
        p1.A();
        p1.A();
        p1.B();
        p1.B();
        
        Print p2 = new Print();
        p2.delimiter = "****";
        p2.A();
        p2.A();
        p2.B();
        p2.B();
        
        
        p1.A();
        p2.A();
        p1.A();
        p2.A();
    }
}Copy to clipboardErrorCopied

 

클래스의 static 변수에는 접근이 불가능하다.

class Accounting {
    public double valueOfSupply;
    
    // 메서드
    public Accounting(double valueOfSupply) {
        this.valueOfSupply = 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(10000.0);
        
        Accounting a2 = new Accounting(10000.0);
        
        System.out.println("Value of supply : " + a1.valueOfSupply);
        System.out.println("Value of supply : " + a2.valueOfSupply);
        
        System.out.println("VAT : " + a1.getVAT());
        System.out.println("VAT : " + a2.getVAT());
        
        System.out.println("Total : " + a1.getTotal());
        System.out.println("Total : " + a2.getTotal());
    }
}

변수 및 메서드

중복 선언을 피하며 성격이 같은 것들끼리 묶어 코드를 짠다.
(반복되는 연관 코드를 메서드로, 지역변수를 전역변수로 만들기)

public class MyOOP {
    public static void main(String[] args) {
        delimiter = "----";
        printA();
        printA();
        printB();
        printB();
        
        delimiter = "****";
        printA();
        printA();
        printB();
        printB();
    }
    
    public static String delimiter = "";
    public static void printA() {
        System.out.println(delimiter);
        System.out.println("A");
        System.out.println("A");
    }
    
    public static void printB() {
        System.out.println(delimiter);
        System.out.println("B");
        System.out.println("B");
    }
}

static

인스턴스에서 클래스 변수의 값을 바꾸면 기존 클래스의 클래스 변수 값도 바뀐다. (반대도 동일함, 서로 연결됨)
그러나 인스턴스에서 인스턴스 변수의 값을 바꿔도 기존 클래스의 인스턴스 변수 값은 바뀌지 않는다.

  • static (O)
    클래스 소속
  • static (X)
    인스턴스 소속 (클래스 복제)
class Foo {
    public static String classVar = "I class var";
    public String instanceVar = "I instance var";
    public static void classMethod() {{
        System.out.println(classVar); // OK
//        System.out.println(instanceVar); // Error (클래스 메서드 안에서는 인스턴스 변수에는 접근할 수 없음) 
    }
    public void instanceMethod() {
        System.out.println(classVar); // OK
        System.out.println(instanceVar); // OK
    }
}

public class StaticApp {
    public static void main(String[] args) {
        System.out.println(Foo.classVar); // OK
//        System.out.println(Foo.instanceVar); // Error (인스턴스는 인스턴스를 통해 사용하도록 고안된 변수)
        Foo.classMethod();
//        Foo.instanceMethod(); // 인스턴스 메서드는 인스턴스 소속이기에 클래스를 통해 접근하는 것은 금지됨
        
        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
        
        f1.instanceVar = "changed by f1";
        System.out.println(f1.instanceVar); // changed by f1
        System.out.println(f2.instanceVar); // I instance var
    }
}

생성자 및 this

  • 생성자
    클래스명과 동일하게 생성 (인스턴스 생성 시 초기값 지정)
  • this
    생성한 인스턴스를 가리키는 이름
// MyOOP.java
public class MyOOP {
    public static void main(String[] args) {
        Print p1 = new Print("----");
        p1.A();
        p1.A();
        p1.B();
        p1.B();
        
        Print p2 = new Print("****");
        p2.A();
        p2.A();
        p2.B();
        p2.B();
        
        
        p1.A();
        p2.A();
        p1.A();
        p2.A();
    }
}Copy to clipboardErrorCopied
// Print.java
class Print{
    public String delimiter = ""; // this가 가리키는 위치
    
    public Print(String delimiter) {
        this.delimiter = delimiter;
    }
    
    public void A() {
        System.out.println(this.delimiter);
        System.out.println("A");
        System.out.println("A");
    }
    
    public void B() {
        System.out.println(this.delimiter);
        System.out.println("B");
        System.out.println("B");
    }
}

참고