nathan_H

[Java] 빠르게 정리하는 자바 클래스 본문

Programming Laguage/Java

[Java] 빠르게 정리하는 자바 클래스

nathan_H 2020. 4. 28. 19:10

들어가기전

객체 개념

  • 물리적으로 존재하는 것(자동차, 책 등등)
  • 추상적인 것 중 자신의 속성과 동작을 가지는 모든 것
  • 객체는 필드과 메소드로 구성되어 자바 객체 모델링이 가능함
    • 현실 세계의 객체를 소프트웨어의 객체로 모델링 하는 과정

객체 지향 프로그래밍

  1. 캡슐화

    https://docsplayer.org/104244489-Chapter-01-html.html

    • 객체의 필드, 메소드를 하나로 묶고, 실제 구현 내용을 감추는 것
    • 외부 객체는 객체 내부 구조를 알지 못하며 객체가 노출해 제공하는 필드와 메소드만 이용 가능
      • 필드와 메소드를 캡슐화 하여 보호하는 이유는 외부의 잘못된 사용으로 인해 객체가 손상되지 않도록 하기 위함
    • 자바 언어는 캡슐화된 멤버를 노출 시킬 것인지 숨길 것인지 결정하기 위해 접근 제한자(Access Modifier) 사용
  2. 상속

    • 상위(부모)객체의 필드와 메소드를 하위(자식) 객체에게 물려 주는 것
    • 하위 객체는 상위 객체를 확장해서 추가적인 필드와 메소드를 가질 수 있음
    • 상속의 효과
      1. 반복된 코드의 중복을 줄임
      2. 유지 보수의 편리성 제공
      3. 객체의 다형성 구현
  3. 다형성

    • 같은 타입이지만 실행 결과가 다양한 객체를 대입할 수 있는 성질
    • 부모 타입에는 모든 자식 객체가 대입
    • 인터페이스 타입에는 모든 구현 객체가 대입
    • 효과
      • 객체를 부품화

객체 vs 클래스

  • 현실세계 : 설계도 → 객체
  • 자바 : 클래스 → 객체
  • 자바에서의 객체는 개발자가 클래스를 통해 설계를 진행한 후 클래스를 인스턴스화 시켜 실제 사용이 이루어짐
    • 하나의 객체는 여러 개의 인스턴스를 만들 수 있음

자바 클래스 선언


  • 소스 파일당 하나의 클래스를 선언하는 것이 관례
    • 두 개 이상의 클래스도 선언 가능하지만 파일당 하나가 관례
    • 소스 파일 이름과 동일한 클래스만 public으로 선언 가능
// Nathan.java

public Nathan {

   String name;
   int age;
}

클래스 생성


  • new 연산자와 함께 힙 메모리에 생성이 됨
    • new 연산자는 객체를 생성한 후, 객체 생성 번지를 넘겨줌
Nathan n1 = new Nathan();
  • 그리고 객체를 생성하고 저장되는 변수의 값은 객체의 주소를 저장하기 때문에 변수는 객체를 "참조"하게 됨

https://programmer-seva.tistory.com/76

스택 영역


  • 함수가 호출될 때 메모리에 할당, 지역 변수, 매개변수
public class StackMemoryTest {

    public static void m1(int a) {
        m2(++a);
        System.out.printf("m1() : %d \n", a);
    }
    public static void m2(int a) {
        m3(++a);
        System.out.printf("m2() : %d \n", a);
    }

    public static void m3(int a) {
        ++a;
        System.out.printf("m3() : %d \n", a);
    }

    public static void main(String[] args) {
        int a = 20;
        m1(a);
        System.out.printf("main() : %d \n", a);
    }
}

결과

m3() : 23 
m2() : 22 
m1() : 21 
main() : 20
  • m1을 호출 한 후 main함수에 a 값을 찍으면 힙 영역으로 인해 값이 바뀌지 않고 그대로 출력이 됨
  • m1, m2, m3도 마찬가지로 매개변수로 받은 값이 그대로 찍힘

힙 영역


  • 함수 호출이 끝나도 유지, 참조 형태의 변수, 인스턴스 변수, 객체, 배열
public class HeapMemoryTest {

    public static int[] m1(int a) {
        int[] arr = m2(a+1);
        arr[2] = a;
        return arr;
    }

    private static int[] m2(int a) {
        int[] arr = m3(a+1);
        arr[1] = a;
        return arr;
    }

    private static int[] m3(int a) {
        int[] arr = new int[3];
        arr[0] = a;
        return arr;
    }

    public static void main(String[] args) {
        int[] arr = m1(100);
        for (int i = 0; i < arr.length; i++) {
            System.out.printf("arr[%d] = %d\n", i, arr[i]);
        }
    }
}

출력

arr[0] = 102
arr[1] = 101
arr[2] = 100
  • 위 예시는 배열을 참조해서 실행이 되기 때문에 arr의 값이 m1, m2, m3로 인해 저장된 값으로 출력이 이루어짐

클래스 구성요소


필드


  • 객체의 "데이터"가 저장되는 곳
    • 객체의 "상태"라고도 표현

필드의 내용

  • 객체의 고유 데이터
  • 객체가 가져야 할 부품 객체
  • 객체의 현재 상태 데이터
public class Car {
     String company; // 고유 데이터
     int rmp;        // 상태
     Engine engine;  // 부품 객체
}
  • 위 코드 예시를 들면 차라는 객체에는 회사라는 고유 데이터 값과 rmp이라는 상태, Engine이라는 부품 객체를 포함해 데이터가 저장이 이루어질 수 있음

사용 예시

public class Car {
    String company = "tesla";
    String model = "ModelS";
    String color = "black";
    int maxSpeed = 350;

    public static void main(String[] args) {
        Car nathanCar = new Car();

        System.out.println("Company : " + nathanCar.company);
        System.out.println("Model Name : " + nathanCar.model);
        System.out.println("Color : " + nathanCar.color);
        System.out.println("Max speed : " + nathanCar.maxSpeed);
    }
}

결과

Company : tesla
Model Name : ModelS
Color : black
Max speed : 350

필드의 기본 초기값

https://m.blog.naver.com/PostView.nhn?blogId=heartflow89&logNo=220956313502

public class FieldInitValue {
    //필드
    byte byteField; short shortField; int intField; long longField;
    boolean booleanField;
    char charField;
    float floatField;
    double doubleField;
    int[]  arrField;
    String referenceField;

    public static void main(String[] args) {
        FieldInitValue fiv = new FieldInitValue();
        System.out.println("ByteField : " + fiv.byteField);
        System.out.println("ShortField : " + fiv.shortField);
        System.out.println("IntField : " + fiv.intField);
        System.out.println("LongField : " + fiv.longField);
        System.out.println("BooleanField : " + fiv.booleanField);
        System.out.printf("charField: \\u%04X%n", (int)fiv.charField);
        System.out.println("floatField: " + fiv.floatField);
        System.out.println("doubleField: " + fiv.doubleField);
        System.out.println("arrField: " + fiv.arrField);
        System.out.println("referenceField: " + fiv.referenceField);
    }
}

출력

ByteField : 0
ShortField : 0
IntField : 0
LongField : 0
BooleanField : false
charField: \u0000
floatField: 0.0
doubleField: 0.0
arrField: null
referenceField: null

생성자


  • new 연산자에 의해 호출되어 객체의 초기화 담당
    • 필드의 값 설정
    • 메소드 호출해 객체를 사용할 수 있도록 준비하는 역할 수행

생성자 종류

기본 생성자

  • 모든 클래스는 생성자가 반드시 존재하며 하나 이상 가질 수 있음
  • 생성자 선언을 생략하면 컴파일러는 다음과 같은 기본 생성자 추가

생성자 선언

  • 개발자가 직접 생성하는 생성자로 컴파일러는 컴파일 시 선언된 생성자가 있으면 기본 생성자를 선언하지 않음
  • 초기화 값 없이 선언된 필드는 객체를 생성될 때 기본값으로 자동 설정되는데, 특정 값으로 필드를 초기화 할 수도 있음
    • 매개 변수와 필드명이 같은 경우 this 사용
People p1 = new People("nathan", 23);
People p2 = new People("홍길동", 24);

그렇다면 생성자 다양화 왜 필요할까?


  1. 객체 생성할 때 외부 값으로 객체를 초기화 할 필요
  2. 외부 값이 어떤 타입으로 몇 개가 제공될 지 모름(생성자도 다양화)
  • 이로 인해 자바는 생성자 오버로딩 기능을 제공해 생성자 다양화를 시킬 수 있음
    • 오버로딩 : 매개변수의 타입, 개수, 순서가 다른 생성자를 여러 개 선언
package object.field;

public class Car {
    String company = "tesla";
    String model = "ModelS";
    String color = "black";
    int maxSpeed = 350;

    public Car(String company) {
        this.company = company;
    }

    public Car(String model, String color) {
        this.model = model;
        this.color = color;
    }

    public Car(String model, String color, int maxSpeed) {
        this.model = model;
        this.color = color;
        this.maxSpeed = maxSpeed;
    }
}

메소드


  • 객체의 동작을 하는 역할
    • 객체의 "행위"라고도 표현

메소드 선언

리턴타입 메소드이름 (매개변수, ..) {
    // 실행 코드 작성
}
public class Calculator {

    void powerOn() {
        System.out.println("Power On!");
    }

    int plus(int x, int y) {
        return x + y;
    }

    double divide(int x, int y) {
        if (y == 0) {
            throw new IllegalArgumentException("0으로 나눌 수 없음.");
        }

        return x / y;
    }

    void powerOff() {
        System.out.println("Power Off..");
    }

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        calculator.powerOn();;
        System.out.println("result 1 : " + calculator.plus(5 ,6));

        byte x = 10;
        byte y = 4;
        System.out.println("result 2 : " + calculator.divide(x, y));
        calculator.powerOff();
    }
}

결과

Power On!
result 1 : 11
result 2 : 2.0
Power Off..

메소드 오버로딩

  • 클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것
  • 하나의 메소드 이름으로 다양한 매개변수 값을 받기 위해 사용
  • 오버로딩 조건 : 매개변수의 타입, 개수, 순서가 달라야 함
class Nathan {
    void printNathan() {
        }

    void printNathan(int weight) {
        }

    void printNathan(int age) {
        }

    void printNathan(int age, int weight) {
        }

}

인스턴스 멤버와 this


  • 인스턴스 멤버
    • 객체마다 가지고 있는 필드와 메소드
  • 인스턴스 멤버는 객체 소속 멤버이기 때문에 객체 없이 사용 불가능
  • this 키워드
    • 객체(인스턴스) 자신의 참조(번지)를 가지고 있는 키워드
    • 객체 내부에서 인스턴스 멤버임을 명확히 하기 위해 this 사용
    • 매개변수와 필드명이 동일할 때 인스턴스 필드임을 명확히 하기 위해 사용

예시 코드

class Nathan {
   int age;
   int height;

   Nathan(int age, int height) {
        this.age = age;
        this.height = height;
   }

정적 멤버와 static


정적 멤버


  • 클래스에 고정된 필드와 메소드
    • 정적 필드, 정적 메서드
  • 정적 멤버는 클래스에 소속된 멤버
    • 객체 내부에 존재하지 않고, 메소드 영역에 존재
    • 정적 멤버는 객체를 생성하지 않고 클래스로 바로 접근해 사용
public class Calculator {

    static double pi = 3.14159;

    static int plus(int x, int y) {
        return x + y;
    }

    static int minus(int x, int y) {
        return x - y;
    }

    public static void main(String[] args) {
        double result1 = 10 * 10 * Calculator.pi;
        int result2 = Calculator.plus(10, 5);
        int result3 = Calculator.minus(10, 5);

        System.out.println("result 1 : " + result1);
        System.out.println("result 2 : " + result2);
        System.out.println("result 3 : " + result3);
    }
}

인스턴스 멤버 선언 vs 정적 멤버 선언


  • 필드

    • 인스턴스 필드 : "객체 마다" 가지고 있어야 할 데이터

    • 정적 필드 : 공용적인 데이터

      public class Calculator {
      
        static double pi = 3.14159; // 계산기에 사용되는 pi 값은 항상 동일
        String color;               // 계산기별 색은 상이할 수 있음
      }
    • 정적 필드의 경우 공용적으로 사용되는 데이터이기 때문에 외부에서 해당 정적 필드를 사용 시 주의를 해야함

      • 정적변수 사용 시 final 을 붙여 변경 불가능하게 해줄 수도 있음
  • 메소드

    • 인스턴스 메소드 : 인스턴스 필드로 작업해야 할 메소드
    • 정적 메소드 : 인스턴스 필드로 작업하지 않는 메소드
    • 정적 메소드의 경우 객체 내부에 존재하는 것이 아니기 때문에 인스턴스 필드에 접근하지 못함!

정적 초기화 블록


  • 클래스가 메소드 영역에서 로딩될 때 자동으로 실행되는 블록
    • 정적 필드의 복잡한 초기화 작업과 정적 메소드 호출 가능
    • 클래스 내부에 여러 개가 선언되면 선언된 순서대로 실행
  • 정적 초기화가 왜 필요한가?
    • 정적 필드에 초기화 작업이 필요한 경우가 존재할 수 있음
    • 정적 필드의 경우 객체 생성 없이도 사용할 수 있어야 하므로 생성장에서 초기화 작업을 할 수 없음
      • 생성자는 객체 생성 시에만 실행되기 때문에
    • 그래서 자바는 정적 블록을 통해 초기화 작업 기능을 제공
public class Car {
    static String company = "tesla";
    static String model = "ModelS";
    static String info;

    static {
        info = company + " - " + model;
    }
}

정적 메소드와 정적 블록 사용시 주의할 점


  • 정적 메소드와 블록은 객체가 없이도 실행이 가능하기 때문에 블록 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없음
    • 당연히 객체 자신을 참조하는 this도 사용 불가

싱글톤


  • 프로그램을 짤 때 가끔 단 하나의 객체만 만들도록 보장하는 경우가 존재하는데, 이 객체를 바로 싱글톤이라고 함
  • 싱글톤 만드는 방법
    1. 외부에서 new 연산자로 생성자를 호출할 수 없도록 막기
      • private 접근 제한자를 생성자 앞에 붙임
    2. 클래스 자신의 타입으로 정적 필드 선언
      • 자신의 객체를 생성해 초기화
      • private 접근 제한자 붙여 외부에서 필드 값 변경 불가능하도록
    3. 외부에서 호출될 수 있는 정적 메소드인 getInstance() 선언
      • 정적 필드에서 참조하고 있는 자신의 객체 리턴

예시 코드

public class Singleton {

    private static Singleton singleton = new Singleton();

    private Singleton() { }

    static Singleton getInstance() {
        return singleton;
    }

    public static void main(String[] args) {
//        Singleton obj1 = new Singleton();  // 컴파일 에러
//        Singleton obj2 = new Singleton();  // 컴파일 에러
        Singleton obj1 = Singleton.getInstance();
        Singleton obj2 = Singleton.getInstance();

        if (obj1 == obj2) {
            System.out.println("같은 Singleton 객체");
        } else {
            System.out.println("다른 Singleton 객체");
        }
    }
}

출력

같은 Singleton 객체

final 필드와 상수


final 필드


  • 최종적인 값을 가지고 있는 필드로써 값을 변경할 수 없는 필드
  • final 필드의 딱 한 번의 초기값 지정 방법
    • 필드 선언 시
    • 생성자
  • 생성자는 final 필드의 최종 초기화를 마쳐야 하는데, 만약 초기화 되지 않는 final 필드를 남겨두면 컴파일 에러가 남

성공

public class Person {

    final String nation = "Korea";
    final String ssn;
    String name;
    int age = 25;

    public Person(String ssn, String name) {
        this.ssn = ssn;
        this.name = name;
    }
}

컴파일 에러

package object.staticfinal;

public class Person {

    final String nation = "Korea";
    final String ssn;
    String name;
    int age = 25;

    public Person(String name) {
        this.name = name;
    }
}

상수 (static final)


  • 상수 : 불변의 값 (원주율 파이, 지구의 무게 및 둘레 등등)
    • final
      • 객체마다 가지는 불변의 인스턴스 필드
      • final을 상수라고 부르지 않는 이유
        • 불변의 값은 객체마다 저장할 필요가 없는 공용성을 띄고 있으며, 여러 가지 값으로 초기화될 수 없기 때문
        • final 필드는 객체마다 저장되고, 생성자의 매개값을 통해서 여러 가지 값을 가질 수 있기 때문에 상수가 될 수 없음
    • 상수 (static final)
      • 객체마다 가지고 있지 않음
      • 메소드 영역에 클래스 별로 관리되는 불변의 정적 필드
      • 공용 데이터로서 사용
  • 상수 이름은 전부 대문자로 작성
  • 다른 단어가 결합되면 _로 연결
    • enum이 나오기 전에 static final로 상수를 표현함
    • enum이란?

예제 코드

public class Earth {
    static final double EARTH_RADIUS = 6400;
    static final double EARTH_SURFACE_AREA;

    static  {
        EARTH_SURFACE_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS;
    }

    public static void main(String[] args) {
        System.out.println("지구의 반지름 : " + Earth.EARTH_RADIUS + " km");
        System.out.println("지구의 표면적 : " + Earth.EARTH_SURFACE_AREA + " km^2");
    }
}

출력

지구의 반지름 : 6400.0 km
지구의 표면적 : 5.147185403641517E8 km^2

접근 제한자


https://worri.tistory.com/98

  • 자바에서는 접근 제한자를 활용해 객체의 메소드 생성자, 필드들에 대해 접근을 제한할 수 있음
    • 클래스 제한 : 다른 패키지에서 클래스를 사용하지 못하도록 함
    • 생성자 제한 : 클래스로부터 객체를 생성하지 못하도록 함
    • 필드와 메소드 제한 : 특정 필드와 메소드를 숨김 처리
  • 초반에 언급한 객체 지향 프로그램에서 캡슐화를 함에 있어 위 접근 제한자를 사용해 진행
  • 종류는 위 그림과 같이 4가지 존재

마무리


  • 본 내용은 자바에서의 클래스의 "기본 개념"에 대해서 정리한 내용이다. 위 내용에서 몇 가지는 포스팅 하나 각잡고 해도 부족할 정도로 깊게 들어갈 수 있는 내용들도 많다. 또한 객체 지향 프로그램에 있어 다양한 의견과 식견이 존재하기 때문에 추후에 "객체 지향"이라는 주제로 포스팅도 해볼까 한다.

[Java] 클래스(Class) - 기본 개념

https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=50563128

Comments