본문 바로가기

개발(합니다)/Java&Spring

[java-기초-06] 클래스

반응형

객체 지향 프로그래밍

  • 현실에서 제품을 만드는 과정과 유사하여 제품을 만들기 위해 부품을 먼저 만들고 부품들을 조립해서 제품을 완성하는 방식
  • 소프트웨에서도 부품에 해당하는 객체들을 만들어 하나의 완성된 프로그램을 만드는 기법을 객체 지향 프로그래밍(OOP)라 한다.
  • 공장에서 설계도를 가지고 부품을 조립하여 제품을 출하하는 개념과 유사하다.

클래스란?

  • 공장에서 부품과 제품을 만들기 위한 설계도이다.
  • 실제하지 않지만 객체를 만들기 위해서 미리 정의해둔 필드와 메서드들이다.

객체란?

  • 공장에서 설계로를 가지고 만든 부품과 제품이다.
  • 실제하고 설계도를 기반으로 필드와 메서드들이 실제 동작한다.

객체 프로그래밍의 특징

  1. 캡슐화 : 객체의 필드, 메서드를 하나로 묶고, 실제 구현 내용을 감추는 것을 말한다. 외부 객체는 객체 내부의 구조를 알지 못하며 객체가 노출해서 제공하는 필드와 메서드만 이용할 수 있다.
  2. 상속 : 부모(조상)이 가진 재산을 자식에게 물려주는 것으로 상위 객체가 가진 필드와 메서드를 자식이 사용할 수 있고 다시 재정의할 수도 있다.
  3. 다형성 : 같은 타입이지만 실행 결과가 다양한 객체를 이용하는 성질을 말한다. 부모 타입에는 모든 자식 객체가 대입될 수 있고 인터페이스 타입에는 모든 구현 객체가 대입될 수 있다.

객체와 클래스

  • 클래스를 설계하고 설계 된 클래스를 가지고 사용할 객체를 생성한 후 생성된 객체를 사용한다.
  • 관례적으로 클래스 이름이 단일 단어라면 첫 자를 대문자로 하고 나머지는 소문자로 작성한다.
  • 파일 이름과 동일한 이름의 클래스 선언에만 public 접근 제한자를 붙일 수 있고 가급적 소스 파일 하나당 동일한 이름의 파일 클래 하나를 선언하는게 좋다.
      // Car.java
      public class Car {
          String name; // 클래스 필드 : 객체의 속성을 의미하며 데이터 저장
          int speed; // 클래스 필드

          int val;

          public Car() { // 기본 생성자 : 생성자는 프로그래머가 만들지 않으면 컴파일러가 자동으로 하나 만들어준다.
              // 객체 생성 시 초기화 역할
          }

          public Car(String name) { // 매개변수를 받는 생성자 
              this.name = name // this는 해당 객체를 의미함 : name이 같으므로 어떤 name을 의미하는지 명시
          }

          public Car(String name, int speed) {
              this(name); // 중복 코드를 없애기 위해 name은 다른 생성자 호출 : this와 this()는 다르다.
              this.speed = speed;
          }

          public void open() { // 메서드 : 객체의 기능을 의미하며 CRUD 조작을 함

          }

          public int value(int ... values) { // 매개 변수의 수를 모를 때 받는 가변인자이고 배열([])로 전달 받아진다.
              for (int i = 0; i < values.length; i++) {
                  this.val += values[i];
              }
              return this.val; // 객체가 가지고 있는 val을 return 함
          }

      }

      // Test.java
      public class Test {
          public static void main(String args[]) {
              Car c = new Car(); // 객체 생성

              c.value(1,2,3,4); // 가변인자를 가진 value 호출 
              c.value(1,2,3,4, 5); // 가변인자를 가진 value 호출 
          }
      }

객체의 메서드 호출

  • 객체 내부에서의 호출 : method2() -> method1()
  • 객체 외부에서의 호출 : c.method2()

메서드 오버로딩

  • 같은 이름을 가진 메서드의 매개 변수의 타입, 개수, 순서에 따라 호출하는 메서드가 달라짐
    int plus(int x, int y) {    }
    int plus(double x, int y) {    }
    int plus(int x, int y, int z) {    }

정적 멤버와 static

  • 정적은 고정되어 있다는 의미로 정적 멤버는 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메서드를 말한다.
  • 프로그램이 시작하고부터 종료될 때까지 공유 메모리 영역에 정적 멤버를 올려두고 어디서든 참고해서 쓸 수 있다.
  • 필드의 경우 객체가 가지고 있어야 할 데이터라면 인스턴스 필드로 선언하고, 객체마다 가지고 있을 필요성이 없는 공용적인 데이터라면 정적 필드로 선언하는 것이 좋다. => PI같은 데이터
  • 메서드의 경우 인스턴스 필드를 이용해서 실행해야 한다면 인스턴스 메서드로 선언하고, 인스턴스 필드를 이용하지 않는다면 정적 메서드로 선언한다. => 덧셈, 뺄셈의 기능은 정적 메서드로 선언하는게 좋다.
    public class Test {
        // 정적 필드
            static int x = 10;

        // 정적 메소드
            static int plus(int x, int y) {}


        int field1;
        void method1() {}

        static int field2;
        static void method2() {}

        static { // 정적 블록
            field1 = 10; // 컴파일 에러
            method1(); // 컴파일 에러

            field2 = 10; // 같은 정적만 가능 
            method2(); // 같은 정적만 가능

        }

        static void Method3() {
            this.field1 = 10; // 컴파일 에러
            this.method1(); // 컴파일 에러

            field2 = 10; // 같은 정적만 가능
            method2(); // 같은 정적만 가능

        }

        public void static main(String args[]) { // main도 static이라 바로 field1에 접근할 수 없어서 객체를 생성하고 접근해야 한다.
            Test t = new Test();
            t.field1 = 20;

        }
    }

싱글톤

  • 객체가 단 하나만 생성됨을 보장하는 객체를 싱글톤이라고 한다.
  • 객체를 아무리 생성해도 같은 객체를 반환하여 같은 객체임을 보장한다.
    public class Test {

        // 정적 필드
        private static Test singleton = new Test();

        // 생성자
        public Test() { }

        // 정적 메서드
        public Test getInstance() {
            return singletone;
        }
}

final 필드

  • 최종의 마지막의 뜻으로 최종적인 필드, 메서드임을 의미하여 객체가 생성되고 한번 초기화 되면 바꿀 수 없음을 말한다.
  • final 필드는 객체마다 저장되고, 생성자의 매개값을 통해서 여러 가지 값을 가질수 있기 때문에 상수가 될 수 없다.

상수(static final)

  • 불변의 값을 상수라고 하며 선언과 동시에 한번 초기값이 저장되면 변경할 수 없다.

패키지

  • 클래스를 체계적으로 관리하기 위해 패키지를 사용하며 파일을 넣어두는 폴더라고 생각하면 된다.
  • package 상위패키지.하위패키지.클래스; 로 작성된다.
  • 회사에서는 보통 도메인 이름을 패키지로 한다. => com.otrodevym.test
  • 규칙
    • 숫자로 시작해서는 안 되고, _, $를 제외한 특수 문자를 사용해서는 안 된다.
    • java로 시작하는 패키지는 자바 표준 API에서만 사용하므로 사용해서는 안 된다.
    • 모두 소문자로 작성하는 것이 관례이다.

패키지 선언이 포함된 클래스 컴파일

java의 컴파일 된 class 파일 위치를 잡는 방법이다.

  • cmd 기준
    • javac -d . // 현재 폴더 내에 생성
    • javac -d ..\bin // 현재 폴더와 같은 위치의 bin 폴더에 생성
    • javac -d C:\Temp\bin // C:\Temp\bin 폴더에 생성

접근 제한자

접근 제한 적용 대상 클래스 내부 동일 패키지 하위 클래스 그 외 영역
public 클래스, 필드, 생성자, 메서드 O O O O
protected 필드, 생성자, 메서드 O O O X
default 클래스, 필드, 생성자, 메서드 O O X X
private 필드, 생성자, 메서드 O X X X
  • public : 제한 없이 접근 가능
  • protected : 본인과 하위(자식) 클래스와 동일 패키지에서 접근 가능
  • default : 본인과 동일 패키지에서만 접근 가능
  • private : 본인만 접근 가능

Getter와 Setter 메서드

  • 일반적으로 객체 지향 프로그래밍에서 객체의 외부에서 객체의 데이터를 읽고 변경할 수 있으면 객체의 무결성 결점이 깨질 수 있기 때문에 객체의 데이터는 객체 외부에서 직접적으로 접근하는 것을 막는다.
      public class Car {
          // 필드
          private int speed;
          private boolean stop;
    
          // 생성자
          public Car() {        }
    
          // getter 메서드
          public int getSpeed() {
              return speed;
          }
    
          // setter 메서드
          public void setSpeed(int speed) {
              if(speed < 0) {
                  this.speed = 0;
                  return ;
              }else {
                  this.speed = speed;
              }
          }
      }

어노테이션

  • 어노테이션은 메타 데이터라고 볼 수 있으며 메타데이터란 애플리케이션이 처리해야 할 데이터가 아니라, 컴파일 과정과 실행 과정에서 코드를 어떻게 컴파일하고 처리할 것인지 알려주는 정보이다.
  • 어노테이션의 용도
    • 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공
    • 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있드록 정보를 제공
    • 실행 시(런타임 시) 특정 기능을 실행하도록 정보를 제공
  • 빌드 시 자동으로 XML 설정 파일을 생성하거나, 배포를 위해 JAR 압축 파일을 생성하는데에도 사용된다.
  • 인터페이스를 정의하는 방법과 유사하다.
public @interface AnnotationName {
    String elementName1();

    int elementName2() default 5;

    @AnnotationName(elementName1 = "값", elementName2 = 10)
    class Test {

    }
}

어노테이션 적용 대상(@Target)

  • 적용될 대상을 지정 할 때 @Targe을 사용한다.
  • @Target의 기본 엘리먼트인 value는 ElementType 배열을 값으로 가지며 복수로 지정할 수 있다.
    @Target({ElementType.TYPE, ElementType.FIELD})
    public @interface AnnotationName{}
ElementType열거 상수 적용 대상
TYPE  클래스, 인터페이스, 열거 타입
ANNOTATION_TYPE 어노테이션
FIELD 필드
CONSTRUCTOR 생성자
METHOD 메서드
LOCAL_VARIABLE 로컬 변수
PACKAGE 패키지

어노테이션 유지 정책(@Retention)

  • 사용 용도에 따라 어느 범위까지 유지할 것인지를 지정해야 한다,
  • 소스상에서만 유지할 건지, 컴파일된 클래스까지 유지할 건지, 런타임 시에도 유지할 건지를 지정해야 한다.
  • 리플렉션이란 런타임 시에 클래스의 메타 정보를 얻는 기능을 말한다.
    @Target({ElementType.TYPE, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AnnotationName{}
RetentionPolicy 열거 상수 설명
SOURCE 소스상에서만 어노테이션 정보를 유지한다. 소스 코드를 분석할 때만 의미가 있으며, 바이트 코드 파일에는 정보가 남지 않는다.
CLASS 바이트 코드 파일까지 어노테이션 정보를 유지한다. 하지만 리플렉션을 이용해서 어노테이션 정보를 얻을 수는 없다.
RENTIME 바이트 코드 파일까지 어노테이션 정보를 유지하면서 리플렉션을 이용해서 런타임 시에 어노테이션 정보를 얻을 수 있다.

런타임 시 어노테이션 정보 사용하기

  • 클래스에 적용된 어노테이션 정보를 얻으려면 java.lang.Class를 이용하면 되지만, 필드, 생성자, 메서드에 적용된 어노테이션 정보를 얻으려면 java.lang.reflect 패키지의 Field, Constructor, Method 타입의 배열을 얻어야 한다.
리턴 타입 메소드명(매개변수) 설명
Field[] getFields() 필드 정보를 Field배열로 리턴
Constructor[] getConstructors()  생성자 정보를 Constructor 배열로 리턴
Method[] getDeclaredMethods() 메서드 정보를 Method 배열로 리턴

Class, Field, Constructor, Method를 얻으면 아래 메서드로 어노테이션 정보를 얻을 수 있다.

리턴 타입 매소드명(매개변수)
boolean isAnnotationPresent(Class<? extedns Annotation> annotationClass)
지정한 어노테이션이 적용되었는지 여부. Class에서 호출했을 때 상위 클래스에 적용된 경우에도 true를 리턴한다.
Annotation getAnnotation(Class<T> annotationClass)
지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고 그렇지 않으면 null을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 경우에도 어노테이션을 리턴한다.

Annotation[] getAnnotations()
적용된 모든 어노테이션을 리턴한다. CLass에서 호출했을 때 상위 클래스에 적용된 어노테이션도 모두 포함한다. 적용된 어노테이션이 없을 경우 길이가 0인 배열을 리턴한다.
Annotation[] getDeclaredAnnotations()
직접 적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 어노테이션은 포함되지 않는다.

어노테이션 실습


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.METHOD })  
@Retention(RetentionPolicy.RUNTIME)  
public @interface PrintAnnotation {  

    String value() default "-";
    int number() default 15;

}
  • PrintAnnotation.java

import java.lang.reflect.Method;

public class Service {  
  @PrintAnnotation  
  public void method1() {  
      System.out.println("실행 내용1");  
  }
  @PrintAnnotation
  public void method2() {
      System.out.println("실행 내용2");
  }

  @PrintAnnotation
  public void methdo3() {
      System.out.println("실행 내용3");
  }

  public static void main(String[] args) {
      Service s = new Service();
      s.method1();

      System.out.println("=======================");
      Method[] declareMethods = Service.class.getDeclaredMethods();

      for (Method method : declareMethods) {
          // PrintAnnotation이 적용되었는지 확인
          if (method.isAnnotationPresent(PrintAnnotation.class)) {
              // PrintAnnotation 객체 얻기
              PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);

              // 메소드 이름 출력
              System.out.println("[" + method.getName() + "] ");
              // 구분선 출력
              for (int i = 0; i < printAnnotation.number(); i++) {
                  System.out.print(printAnnotation.value());
              }
              System.out.println();

              try {
                  // 메소드 호출
                  method.invoke(new Service());
              } catch (Exception e) {
              }
              System.out.println();
          }
      }
  }
}
  • Service.java
반응형

'개발(합니다) > Java&Spring' 카테고리의 다른 글

[java-기초-08] 인터페이스  (0) 2021.01.06
[java-기초-07] 상속  (0) 2021.01.05
[java-기초-05] 참조 타입  (0) 2021.01.03
[java-기초-04] 조건문과 반복문  (2) 2021.01.02
[java-기초-03] 연산자  (0) 2021.01.01