상세 컨텐츠

본문 제목

4. 객체지향 프로그래밍의 시작 "클래스" (Java 클래스)

How To Java/Java Tutorial

by 카페코더 2020. 2. 26. 18:50

본문

반응형

취업 준비를 하며 복습한다는 마음으로, 이것을 보는 누군가에게 도움이 되었으면 하는 마음으로
Java-Tutoral을 작성해 봅니다. 입문서와 순서가 잘못되었을 수 있고, 제가 아는 정보가 틀렸을 수 있습니다.
이 글에 대한 잘못된 정보나, 오탈자 등 수정해야 할 항목 혹은 추가해야 할 항목은 댓글로 알려주시면 감사하겠습니다.

이 자료가 올라가는 저장소 :
https://github.com/hwk0911/Java-tutorial

 

hwk0911/Java-tutorial

Java tutorial. Contribute to hwk0911/Java-tutorial development by creating an account on GitHub.

github.com

객체지향 프로그래밍의 시작. 클래스 포스팅이다.

절차 지향 언어(대표적으로 C)를 먼저 공부한 사람이라면, 익숙해지는데 오랜 시간이 걸릴 것이다.

1. 객체지향 프로그래밍 (Object-Oriented Programming : OOP)

프로그램 설계 방법론이자 개념의 일종이다.

결론부터 말하면, 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고
그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다.

단순히 데이터와, 처리 방법으로 나누는 것이 아닌, 객체라는 기본 단위로 나누고
객체들의 상호작용으로 서술하는 방식이다.

절차 지향 프로그래밍이 아닌, 객체지향 프로그래밍을 사용해야 하는 이유는 단순하다.
간단한 알고리즘을 다루는 경우가 아닌, 복잡한 프로그래밍 간단하게 게임을 제작한다 생각해보자.
당장에 테트리스 게임만 봐도, 처음부터 절차 지향으로 순서도를 그리는 것조차 버거울 것이다.

"GOTO문을 사용하면 해결 가능하다"는 의견이 있을 수 있다.
에츠허르 다익스트라는 1968년 "GOTO문의 해로움"이라는 논문을 발표했다.
제목만 봐도 GOTO는 좋지 못한 문법이란 것을 알 수 있다.

객체지향 프로그래밍(이하 OOP)을 위한 몇몇의 개념부터 설명하고자 한다.

1.1 용어 정리

  1. 클래스 
    OOP에서 특정 객체를 생성하기 위해 변수와 메서드를 정의하는 일종의 틀
  2. 객체
    저장공간에서 할당되어 값을 가지거나 식별자에 의해 참조되는 공간을 의미한다.
    변수, 자료 구조, 함수 또는 메서드가 될 수 있다.

    즉 클래스를 메모리에 할당하여 사용하기 위해 사용된다.
    AKA "인스턴스"

  3. 필드
    클래스에 속한 변수

  4. 생성자
    객체를 선언할 때, new 연산자를 통해 객체를 생성하는데,
    객체를 생성할 때 반드시 호출이 되고, 제일 먼저 실행된다.
    정확히 메서드는 아니지만, 메서드라 생각하면 쉽다.
    주로 필드를 초기화하는 역할을 한다.

  5. 메서드
    AKA "멤버 함수". 즉 클래스나 객체에 속한 함수다.
    인스턴스 멤버
    객체 내부의 멤버를 의미한다. (객체의 필드, 객체의 메서드, 객체의 생성자 등)

  6. this
    this는 객체의, 즉 자기 자신의 인스턴스 멤버를 호출할 때 사용된다.

  7. 정적 멤버
    정적 즉 고정된, 변함없는 멤버다.
    클래스에 고정된 멤버로, 객체를 생성하거나 하지 않아도 사용할 수 있는
    필드 또는 메서드를 말한다.

  8. static
    인스턴스를 생성하면, 모든 인스턴스는 서로에 대해 독립적이다.
    따라서 인스턴스 멤버 역시 독립적이다.

    이것을 독립적이지 않고, 클래스 내에 속한, 즉 객체 모두가 공통적인 값을
    유지해야 할 때, static으로 선언한다.

    static으로 선언된 멤버는 인스턴스를 생성하지 않아도 사용이 가능하다.

  9. final
    Java의 대표적인 3대 규약에 속한다. Interface, Abstract, "final"
    final은 Java의 상수를 선언할 때 사용된다. 즉 변하면 안 된다는 뜻이다.

    쉽게 말해 "읽기 전용"이라 생각하면 쉽다.
    메서드를 final로 선언하면, 오버라이드를 제한할 수 있다.

  10. 패키지
    서로 관련이 있는 클래스 또는 인터페이스 등의 묶음이다.
    패키지 내에 선언된 항목의 이름은 패키지 이름과 함께 사용되기 때문에,
    다른 그룹에 속한 클래스와 발생할 수 있는 클래스 이름 간의 충돌을 막아준다.

  11. 접근 제한자
    누가 봐도, 접근을 제한하는 역할을 한다는 것을 알 수 있다.
    소규모의 프로젝트에는 크게 상관없을 수 있다.

    하지만, 대규모의 프로젝트를, 아니 중간 규모 이상의 프로젝트를 진행할 때,
    클래스와 멤버 연관 관계와, 공개 유무 등을 설정할 때 필요하다.

  12. Getter & Setter
    클래스의 특징인 정보은닉을 가장 쉽게 경험할 수 있는 메서드다.
    보통 클래스의 필드를 private선언, Getter, Setter를 public으로 선언하여
    멤버 변수에 접근하게 한다.

  13. 어노테이션
    어노테이션은 Java5부터 추가된 기능이다.
    클래스가 컴파일되거나, 실행될 때, 특정 방식으로 실행되도록 설정하는 역할을 한다.

1.2 클래스

클래스는 OOP에서 특정 객체를 생성하기 위해 변수와 메서드를 정의하는 일종의 틀이다.
쉽게 말해, 우리가 어떤 기능을 구현하는데, 이 기능을 추상화한 것이다.

예를 들어, 동물의 특징에 대한 클래스를 선언해보자.

class Animal {
    int weight;
    int length;
    String kind;

    Animal(int weight, int length, String kind){
        this.weight = weight;
        this.length = length;
        this.kind = kind;
    }

    public void move() {
        
    }

    public void eat() {
        
    }
}
  1. 동물이 갖는 무게, 길이, 종류를 속성(이하 필드)으로 하여 변수로 선언했다.
  2. 동물이 갖는 필드를 생성과 동시에 초기화 가능하도록, 생성자를 선언했다.
    1. 생성자는 객체를 생성할 때, 같이 선언되어야 하는 항목을 지정한다.
    2. 생성자는 한 종류뿐 아니라, 매개 변수의 종류를 다르게 하여 여러 개 선언이 가능하다.
    3. 물론 비워놔도 상관없다.
  3. 동물이 하는 행동에 대한 메서드를 선언했다.

이렇게 추상화되어 선언한 것이 클래스다. 

1.3 객체

객체는 클래스를 실제 메모리에 올려 새로 생성된 인스턴스라 할 수 있다.
값을 가지거나, 식별자에 의해 참조되는 공간을 의미하며, 변수, 자료 구조, 메서드가 될 수 있다.

따라서 메모리가 할당되기 전에는 객체가 존재할 수 없다.

그러면 위의 Animal클래스를 main 함수에서 객체로 선언해보자.

class Animal {
    int weight;
    int length;
    String kind;

    Animal(int weight, int length, String kind){
        this.weight = weight;
        this.length = length;
        this.kind = kind;
    }

    public void move() {

    }

    public void eat() {

    }
}

class Main{
    public static void main(String[] args) {
        Animal horse = new Animal(200,250,"포유류");

        System.out.println("horse 분류 : " + horse.kind);
        System.out.println("horse 무게 : " + horse.weight);
        System.out.println("horse 길이 : " + horse.length);
    }
}
/*
결과 : 
horse 분류 : 포유류
horse 무게 : 200
horse 길이 : 250

Process finished with exit code 0
 */

horse 객체를 선언하고, 생성자를 통해 무게, 길이, 종류를 선언했다.
후에 각 필드가 갖고 있는 데이터를 출력하는 코드다.

1.4 필드, 생성자, 메서드

클래스의 구성요소들이다. 클래스를 선언한 위의 소스를 다시 살펴보자.

  1. 필드
    1. weight
    2. length
    3. kind
  2. 생성자
    1. Animal(int weight, int length, String kind)
  3. 메서드
    1. public void move()
    2. public void eat()

즉 필드는 클래스에 속한 멤버 변수를 의미하고, 생성자는 객체를 생성할 때 필연적으로 사용되는 일종의 메서드다.
메서드는 클래스에 속한 멤버 함수를 의미한다.

객체를 선언하지 않은 상태로는 접근할 수 없다. 단, static으로 선언된 항목에 대해서는 접근이 가능하다.

static에 대해서는 1.6에서 마저 서술하겠다.

1.5 this

우선 생성자를 다시 보자. Animal(int weight, int length, String kind)로 선언되어있다.

여기서 의문점이 생긴다. 변수명이 같다면, 에러가 생기는 게 당연한 게 아닌가?? 컴퓨터는 어떻게
이 변수를 구별하여 사용하는 건가?

바로 그 해답이 this다. 변수를 호출할 때, 앞에 this를 통해 호출이 된다면, 그것은
"클래스 내의 멤버를 호출한다"는 의미로 해석된다. 따라서 this가 붙지 않은 호출은 외부에서 들어오는
매개 변수와 같은 것들로 생각을 하고, this가 붙은 호출은 멤버를 호출하겠다는 의미가 된다.

1.6 정적 멤버, static

static은 직독 하자면 '정적인'의 의미를 갖는다. 즉 static으로 선언된 멤버를
정적 멤버라 한다.

보통의 멤버들이 객체를 생성하면 각 객체마다 독립적인 성향이 있는데,
static으로 선언하게 되면, 클래스의 모든 객체들에 대해 공통적인 성향을 갖는다.

이해를 쉽게 하기 위해 다음 코드를 보자.

class Test{
    static int number;

    Test(int number){
        this.number = number;
    }

    public void getNumber(){
        System.out.println(number);
    }
}

class Main{
    public static void main(String[] args) {
        Test T1 = new Test(1);
        Test T2 = new Test(2);

        T1.getNumber();
        T2.getNumber();
    }
}
/*
결과 :
2
2

Process finished with exit code 0
 */

Test의 객체 T1과 T2를 생성해 주었고, T1의 생성자로 static으로 선언된 number변수를 1로 초기화시켰다.
후에 T2를 생성하며 number를 2로 초기화를 시켜줬다.

결과는 1, 2가 아닌 2, 2가 나왔다. 

이렇듯 static멤버는 클래스의 모든 객체들에 종속된 멤버를 의미한다.

1.7 final

위에서 fianl은 Interface, Abstract와 함께 3대 규약에 해당한다 했다.
(Interface와 Abstract에 대해서는 다음 포스팅에서 다루겠다.)

강제성을 띠고있다. 

OOP의 특징 중 하나는 상속(재활용)이다.
클래스를 선언하고, 앞에 final키워드를 사용하면, 클래스는 더이상 상속되지 못한다.
클래스 뿐 아닌, 필드 메소드에도 사용이 가능하다.

final키워드가 붙을 때 변경점

  1. 클래스
    final키워드가 붙은 클래스를 상속받을 수 없다.
  2. 메서드
    오버라이딩 할 수 없다.
  3. 필드
    1. 원시 자료형 : 더이상 값을 변경할 수 없는 상수로 변한다.
    2. 참조 자료형 : 메모리 내의 다른 객체를 참조할 수 없도록 변한다.

+ 추가
final과 비슷한 finally, finalize 둘은 fianl과 다른 내용이니 헷갈리지 않도록 하자.

간단하게만 설명하고 넘어가자면,
fianlly는 예외처리 try / catch문법에서 항상 실행되는 코드로 선언된 블록이다.

finalize는 객체가 소멸될 때 호출되기로 약속된 메소드다.
이 메소드는 후에 다시 다루지 않을 예정이다. 많은 전문가들이 이 메소드의 사용을
만류하고 있다 배웠다.

1.8 패키지

자바의 패키지는 클래스들의 묶음이라고 위에 간단하게 서술했다.
간단한 하나의 알고리즘을 사용하거나 할 때는 큰 의미가없지만, 규모가 커지게 된다면,
클래스의 이름이 겹치는 순간이 올 수 있다. 이것을 방지하는것이 패키지다.

하나의 디렉토리 처럼 볼 수 있으며, 클래스의 이름이 "패키지 + 클래스명"으로 다뤄지게 된다.
따라서 위의 순간을 방지할 수 있게된다.

예를들어 ArrayList클래스를 살펴보자.
ArrayList를 사용하게되면, import java.util.ArrayList; 를 상단에 선언해주어야 한다.

이것은 java 패키지 내의 util패키지 내의 ArrayList클래스를 import했다고 말할 수 있다.

단순하게 설명하자면,
프로젝트에 패키지들이 존재하고, 각각의 패키지들은 클래스 파일의 묶음으로 생각하면 쉽다.

1.9 접근 제한자

접근 제한자는 객체의 멤버들에게 접근 제한을 거는 키워드다....
너무 당연한 얘기다. 접근 제한자를 보고 접근을 제한하는것! 이라는 얘기는 누구나 할 수 있다.
하지만 더이상 어떤 설명을 덧붙여야 할지 모르겠다. 

"클래스와 멤버 연관 관계와, 공개 유무 등을 설정할 때 필요하다."

Java의 접근 제한자 종류 및 간략한 설명

  1. public
    모든 접근을 허용한다.
  2. protected
    같은 패키지내의 객체와, 상속관계의 객체들만 접근이 가능하다.
  3. default
    같은 패키지내의 모든 객체들이 접근이 가능하다.
  4. private
    현재 객체 내에서만 접근이 가능하다.

접근 제한자.png

위 사진과 같은 개념이라 보면 이해하기 쉽다!

주로 정보 은닉을 위해 사용된다.

1.10 Getters, Setters

게터즈, 세터즈 혹은 게터 세터 라 부른다. 접근 제한자를 누구보다 쉽게 활용 가능하다.
즉 정보 은닉의 대표적인 예시라 볼 수 있다.

class Test{
    public int number;

    public int getNumber(){
        return this.number;
    }

    public void setNumber(int number){
        this.number = number;
    }
}

class Main{
    public static void main(String[] args) {
        Test T1 = new Test();

        T1.setNumber(50);
        System.out.println(T1.number);
    }
}
/*
결과 :
50

Process finished with exit code 0
 */

number를 public으로 선언했을땐, T1의 number를 가져오는것이 가능하다.

그렇다면 private으로 선언해보자.

class Test{
    private int number;

    public int getNumber(){
        return this.number;
    }

    public void setNumber(int number){
        this.number = number;
    }
}

class Main{
    public static void main(String[] args) {
        Test T1 = new Test();

        T1.setNumber(50);
        System.out.println(T1.number);
    }
}
/*
결과 :
Error:(18, 30) java: number has private access in Test
 */

단순히 public -> private으로 바꾼 것 뿐인데, 바로 에러가 발생했다!

private 데이터에 접근했을때 발생하는 에러다. 이것을 위해 getter, setter가 필요하다.
public 으로 선언한 두 메소드를 통해 private으로 처리된 데이터에 간접적으로 접근을 가능하게 한다.

class Test{
    private int number;

    public int getNumber(){
        return this.number;
    }

    public void setNumber(int number){
        this.number = number;
    }
}

class Main{
    public static void main(String[] args) {
        Test T1 = new Test();

        T1.setNumber(50);
        System.out.println(T1.getNumber());
    }
}
/*
결과 :
50

Process finished with exit code 0
 */

이런식으로 getter, setter는 private데이터에 대해 간접적인 접근을 할 수 있게 해준다.

1.11 어노테이션 (Annotation)

Java5부터 도입된 키워드다. 클래스가 컴파일되거나, 실행될 때, 특정 방식으로 실행되도록 설정하는 역할을 한다.
라고 위에 간략하게 서술했다. 

종종 메소드 위에 @블라블라 로 선언 된 무언가를 볼 수있다. 대표적으로 @Override
이것들을 모두 어노테이션이라 부른다.

Java자체적으로 지원하는 어노테이션이 있고, 사용자가 직접 선언한 custom Annotation도 존재한다.
즉 입맛대로 Annotation을 선언하여 사용할 수 있다.

어노테이션의 경우 유효성 검사, 클린 코드, 등 다양한 목적이 있지만,
주로 메타 데이터를 위한 비중이 가장 크다 할 수 있다.

+ 메타 데이터 : 메타 데이터는, 데이터에 대한 데이터이다. 즉 데이터를 설명하기 위한 데이터다.
유사한 방식으로 도서관의 책 배가 방식을 들 수 있다. 

메타 데이터 예시

 

반응형

관련글 더보기

GitHub 댓글

댓글 영역