본문 바로가기

Development/JAVA

(Java) Chapter7. 객체 지향 프로그래밍

1. 오리엔테이션

  객체 지향 프로그래밍(Object-Oriented Programming)

  - 객체란?

    프로그램을 구성하는 로직을 상태와 행위로 그룹화 한 것

    ※ 앞의 6개 챕터는 절차 지향 프로그래밍이라고 함

  - 절차 지향 : 개체를 순차적으로 처리하는 방식 (선형적)

  - 객체 지향 : 개체를 캡슐화하여 상호작용하여 처리하는 방식 (구조적)

  - 객체 지향 프로그래밍 컨셉

    문법 : if나 for문처럼 객체를 만드는 방법

    설계 : 현실의 추상화, 로직의 부품화, 부품들의 은닉화(캡슐화), 부품들간의 연결점 표준화, 로직의 상속성, 로직들의 다형성(오버로딩)

 

2. 클래스와 인스턴스 그리고 객체

  1) 클래스와 인스턴스

  - 클래스 : 설계도, 메소드의 정의

    코딩의 소양은 중복의 제거, 로직이 중복된다면 메소드를 사용.

public class _01_Class {

//    public static void main(String[] args) {
//        System.out.println(10 + 20);
//        System.out.println(20 + 40);
//    }
// refectoring : 기존의 코드와 같게 동작하지만 코드의 내용을 개선하여 효율적으로 만든 행위
    public static void sum(int left, int right) {
        System.out.println(left + right);
    }

    public static void main(String[] args) {
        sum(10, 20);
        sum(20, 40);
    }
}

    ※ 클래스는 한 파일 안에 여러개를 정의할 수 있지만 대표 클래스는 앞에 'public'이라는 접근 제어자를 붙여야 하며 파일명과 같아야 함

    ※ static = static + heap, 전자는 메모리를 공유하고 클래스가 할당, 후자는 메모리를 공유하지 않으며 객체(인스턴스)가 할당함

class Calculator {                                          // Calculator 클래스 정의, 설계
    int left, right;

    public void setOprends(int left, int right) {
        this.left = left;                                   // this. 객체 속성값 입력 시 사용
        this.right = right;
    }

    public void sum() {                                     // 덧셈 메소드 정의, static 이 없음 > 객체 선언이 필요함
        System.out.println(this.left + this.right);         // static 은 각 정의들이 같은 메모리를 공유하여 객체 선언 필요 없음
    }

    public void avg() {                                     // 평균 메소드 정의
        System.out.println((this.left + this.right) / 2);
    }
}

public class _02_Object2 {                                  // public class 는 파일명과 같아야 함. 즉, 대표 클래스
    public static void main(String[] args) {
        Calculator c1 = new Calculator();                           // new 이후가 객체, c1 변수에 담음, 담긴 것들을 인스턴스
        c1.setOprends(10, 20);
        c1.sum();
        c1.avg();

        Calculator c2 = new Calculator();
        c2.setOprends(20, 40);
        c2.sum();
        c2.avg();
    }
}

  ※ 객체 : 변수와 메소드의 집합, 상기 빌드의 class Calculator 하위 영역에 해당함

  - 인스턴스 : 제품, 메소드의 호출

    c1은 new를 통해 인스턴스를 생성하고 담는 변수

  - Calculator : 설계도 (클래스)

    c1 : 제품 (인스턴스)

    left 10, right20 : 상태 (변수)

    sum(), avg() : 행위 (메소드)

  - 즉, 상태와 행위로 이루어진 객체로 만드는 것이 객체지향 프로그램

 

  2) 객체

  - Calculator + c1 : 데이터타입 + 변수(컨테이너, 인스턴스)

    변수에 테이터타입에 맞추어 정보를 입력

  - String[] args : 문자열 데이터 컨테이너

    입력된 정보는 메모리 내에서 그 정보를 위한 자리가 마련되어있다.

  - sum(), avg() : 컨테이너 안에 변수를 입력하면 결과값을 만들어 낼 메소드

    즉, 수학에서 미지수 x, y 자리를 main영역을 정의할 때 마련하고 그 하위에는 변수를 정의해서 어떤 계산식을 가져올 것인지 인스턴스를 통해 클래스 안의 메소드를 불러와서 미리 정의 된 입력 형식에 맞추어 입력, 그리고 실행을 하면 계산 된 결과값을 출력 함

 

3. 클래스 맴버와 인스턴스 맴버

  1) 맴버

  - 객체의 구성원 : 변수, 메소드

  - 클래스의 구성원 : 변수, 메소드

  - 인스턴스의 구성원 : 변수, 메소드

  2) 각 그룹의 구성원 정의

  - 클래스 변수 : 클래스에서 정의한 변수로 하위의 인스턴스는 클래스 변수 정보도 가지게 됨

class Calculator {                                          // Calculator 클래스 정의, 설계
    static double PI = 3.14;
    // 클래스 변수인 base가 추가
    static int base = 0;
    int left, right;

    public void setOprends(int left, int right) {
        this.left = left;                                   // this. 객체 속성값 입력 시 사용
        this.right = right;
    }

    public void sum() {                                     // 덧셈 메소드 정의, static 이 없음 > 객체 선언이 필요함
        // 더하기에 base 의 값을 포함
        System.out.println(this.left + this.right + base);         // static 은 각 정의들이 같은 메모리를 공유하여 객체 선언 필요 없음
    }

    public void avg() {                                     // 평균 메소드 정의
       // 평균치에 base 의 값을 포함시킨다.
        System.out.println((this.left + this.right + base) / 2);
    }
}

    여기에 base의 변수를 다시 정의하면

public class _02_Object2 {               // public class 는 파일명과 같아야 함. 즉, 대표 클래스
    public static void main(String[] args) {
        Calculator c1 = new Calculator();// new 이후가 객체, c1 변수에 담음, 담긴 것들을 인스턴스
        c1.setOprends(10, 20);
        c1.sum();
        c1.avg();
        System.out.println(c1.PI);      // 클래스에 인스턴스를 생성할 필요가 없는 값을 정의 : 클래스 맴버인 변수
        Calculator.base = 10;
        c1.sum();                       // 40

        Calculator c2 = new Calculator();
        c2.setOprends(20, 40);
        c2.sum();                       // 70 (c1에서 정의된 10)
        c2.avg();                       // 35 (c1에서 정의된 10)
        System.out.println(c2.PI);
        Calculator.base = 20;
        c2.sum();                       // 80 (c2에서 base 를 20으로 변경, 20 + 40 + 20)
    }
}

- 클래스 메소드 : 클래스에서 정의되는 메소드로 인스턴스 메소드에 접근 불가, 반대 방향은 접근 가능

class Calculator3 {
    public static void sum (int left, int right) {  // static 이 붙으면 직접적으로 불러올 수 있음(class.meth ())
        System.out.println(left + right);           // static 이 없으면 인스턴스를 지정하여 불러옴(c1, c2)
    }
    public static void avg (int left, int right) {
        System.out.println((left + right) / 2);
    }
}

public class _04_Object4 {
    public static void main(String[] args) {
        Calculator3.sum (10, 20);
        Calculator3.avg(10, 20);

        Calculator3.sum (20, 40);
        Calculator3.avg(20, 40);
    }
}

// 인스턴스를 사용하는 이유는 인스턴스가 다른 인스턴스와 구분된 상태를 가지고 있어야하기 때문에 사용
// 굳이 할 필요 없으면 static 을 사용

 

  - 인스턴스 변수 : 인스턴스마다 다른 값을 가지는 변수, 정의된 클래스에는 영향을 주지 않음

class C1 {
    static int static_variable = 1;
    int instance_variable = 2;
    static void static_static() {
        System.out.println(static_variable);
    }
    static void static_instance() {
        // 클래스 메소드에서는 인스턴스 변수에 접근 할 수 없다.
        // System.out.println(instance_variable);
    }
    void instance_static() {
        // 인스턴스 메소드에서는 클래스 변수에 접근할 수 있다.
        System.out.println(static_variable);
    }
    void instance_instance() {
        System.out.println(instance_variable);
    }
}
public class _05_Member2 {
    public static void main(String[] args) {
        C1 c = new C1();
        // 인스턴스를 이용해서 정적 메소드에 접근 -> 성공
        // 인스턴스 메소드가 정적 변수에 접근 -> 성공
        c.static_static();                      // 1, 인스턴스로 클래스 접근 가능
        // 인스턴스를 이용해서 정적 메소드에 접근 -> 성공
        // 정적 메소드가 인스턴스 변수에 접근 -> 실패
        c.static_instance();                    // 에러 : static 메소드에서 비 static 접근 불가
        // 인스턴스를 이용해서 인스턴스 메소드에 접근 -> 성공
        // 인스턴스 메소드가 클래스 변수에 접근 -> 성공
        c.instance_static();                    // 1
        // 인스턴스를 이용해서 인스턴스 메소드에 접근 -> 성공
        // 인스턴스 메소드가 인스턴스 변수에 접근 -> 성공
        c.instance_instance();                  // 2
        // 클래스를 이용해서 클래스 메소드에 접근 -> 성공
        // 클래스 메소드가 클래스 변수에 접근 -> 성공
        C1.static_static();                     // 1, 클래스를 통해 클래스를 접근 가능
        // 클래스를 이용해서 클래스 메소드에 접근 -> 성공
        // 클래스 메소드가 인스턴스 변수에 접근 -> 실패
        C1.static_instance();                   // 에러 : 클래스가 인스턴스 접근 불가
        // 클래스를 이용해서 인스턴스 메소드에 접근 -> 실패
        // C1.instance_static();                // 에러 : 인스턴스 생성이 되지 않았음
        // 클래스를 이용해서 인스턴스 메소드에 접근 -> 실패
        // C1.instance_instance();              // 에러 : 인스턴스가 인스턴스 접근 불가
    }
}

  ※ 인스턴스 변수 -> Non-Static Field(variable)

  ※ 클래스 변수 -> Static Field

  ※ 결론 : 클래스 > 클래스, 인스턴스 > 인스턴스, 인스턴스 > 클래스는 접근 가능 / 클래스 >  인스턴스 접근 불가 (변수, 메소드 같은 결과)

 

5. 유효범위 (Scope)

  1) 개념 설명

  - 프로그램이 커지면 이름이 충돌하는 상황이 발생하기도 하는데, 해결하기 위한 개념

  - 클래스 안에서 선언된 변수는 클래스 안에서만 영향을 미침

  2) 다양한 유효범위들

  - 디렉터리 : 파일 위계 상 파일 이름이 같다면 충돌을 피하기 위해 이름을 바꾸게 되는데, 디렉터리는 내부의 파일들의 이름이 밖의 파일명과 중복돼도 문제가 없음 

  - 정적 유효범위(Static Scope, Lexical Scope) : 자기 자신 또는 전역변수에만 가지는 고정적인 형태의 유효범위

    static int i = 5;
    static void a() {
        int i = 10;
        b();
    }
    static void b() {  // 정적인 유효범위 : 자기 자신 또는 전역변수에만 접근하는 고정된 형태의 유효범위
        // int i = 30;
        System.out.println(i);
    }

    public static void main(String[] args) {
        a();     // 5, a()의 지역변수가 10, b()는 전역변수 5를 가져오므로 우선함
                 // b()의 지역변수 30이 있다면 30을 가져올 것
                 // 전역변수 i = 5를 삭제하면 에러
    }
}

  - 동적 유효범위(Dynamic Scope) : 메인 영역 내부 또는 호출 메소드의 지역변수에 접근하는 유효범위

class C {
    int v = 10;
    void m() {
        int v = 20;
        System.out.println(v);              // 20, 지역변수가 우선
        System.out.println(this.v);         // 10, 전역변수 지정
    }
}
public class _11_Scope6 {
    public static void main(String[] args) {
        C c1 = new C ();
        c1.m();

    }
}

  ※ 동적 유효범위에서 전역변수를 배제하고 있지만 지정해야한다면 this를 사용

 

6. 동향

  - 유효범위란 변수를 전역변수와 지역변수를 나눠서 좀 더 관리하기 편하도록 함

  - 절차지향 프로그래밍에서는 모든 메소드에서 접근이 가능한 변수의 사용을 금기시하는 경향이 있음 (사용 목적이 분명한 경우에만 전역변수를 사용)

  - 객체지향 프로그래밍은 이러한 문제를 극복하기 위한 노력 > 연관된 변수와 메소드를 그룹핑할 수 있도록 하여 객체 안에서 전역변수를 사용할 수 있도록 함

  - 커진 객체 안에서의 메소드 추출의 경우 전역변수에 의존성이 생기기 때문에 가급적 전역변수의 사용을 자제하는 것이 좋으며 단일 객체가 너무 비대해지지 않도록 적적하게 규모를 쪼개는 것도 중요하다.

 

'Development > JAVA' 카테고리의 다른 글

(Java) Chapter13. Object  (0) 2023.01.23
(Java) Chapter12. 예외  (0) 2023.01.23
(Java) Chapter11. 접근 제어자  (0) 2023.01.17
(Java) Chapter10. API  (0) 2023.01.10
(Java) Chapter8. 생성자와 상속  (0) 2023.01.10