5 minute read

JAVA 객체 지향

자바의 정석 6장 7장

객체지향언어 (OOP)

  1. 캡슐화
  2. 상속
  3. 추상화
  4. 다형성

클래스와 객체 (class & object)

클래스로부터 객체를 만드는 것이 인스턴스화(instantiate), 결과물이 인스턴스 이다.

클래스

클래스명은 대문자로 시작하고 주로 명사. 객체들을 정의해 놓은 설계도로 인스턴스를 만드는데 쓰인다. 객체의 타입을 나타낸다.

객체

속성과 기능의 집합.(iv의 집합) 속성(property; variable(a.k.a변수), attribute, field, state)과 기능(method, function)을 구성원(member)으로 한다.

class AppleWatch{
    // 속성(var)
    int series;
    String material;
    String color;
    int size;
    String band;
    
    // 기능(method)
    void alarm(){
        // 알람설정
    }
    void call(){
        // 전화걸기
    }    
}

변수와 메서드 (variable & method)

객체의 선언 위치에 따라 다르다

변수(Variable)

속성

  • cv(Class Variable): 클래스가 메모리로 올라가면서 생성 static 붙은 애들 + final 붙은 상수
  • iv(Instance Variable): 객체(인스턴스)가 생성되면서 생성
  • lv(Local Variable): 메서드에서 선언된 변수

일반적으로 cv가 클래스 생성되면서 같이 메모리(method 영역)에 올라간다. 그 후에 해당 클래스가 객체를 만들면 iv가 메모리(heap 영역)에 올라간다. 해당 객체에 있는 메서드가 호출되면서 lv가 메모리(stack 영역)에 할당된다.

메서드(Method)

기능

메서드는 [리턴 타입] [메서드명] (선언부) { 구현부 } 로 구성되어있다. 메서드명은 소문자로 시작하고 기능을 나타내는 동사 메서드는 메모리에 call stack 형식으로 관리/호출된다. 리턴 타입은 호출한 곳과 리턴되는 타입(클래스)을 맞춰야한다.

매개변수(param; parameter)

기본형(Primitive), 참조형(Reference)

기본형(primitive type): read only

  • 정수 4개(byte, short, int, long) + 실수 2개(float, double) + 문자 (char) + 논리 (boolean)으로 구성
  • Wrapper Class로 변환된 reference tpye의 이름은 Integer, Character를 제외하면 Capitalization.
  • 실제 값을 저장하는 stack 영역에 저장

참조형(reference type): read & write

  • 기본형 이외에 모두 참조형
  • Null 선언 가능
  • 메모리 주소값을 저장하는 Heap 영역에 저장
class AppleWatch{
    // 속성(var)
    int series;
    String material;
    String color;
    int size;
    String band;
    
    // 기능(method)
    void alarm(){
			System.out.println("//alarm");
    }
    void call(int phoneNumber, String name) {
		System.out.println("//call from " + name + "("+phoneNumber+")");
	}  
}

오버로딩 (overloading)

메서드명이 같을 수 있다?

  • 한 클래스에 서로 이름들이 같은 메서드
  • 리턴 타입은 상관이 없고
  • 매개변수(param) 갯수/타입이 다르다.

생성자 (constructor)

new로 친숙한 그 녀석

인스턴스를 생성할 때 호출되는 초기화(init) 메서드

  • 클래스명과 같은 메서드
  • 리턴 타입은 필요없다
  • 아예 없으면 컴파일 시 자동 생성: [클래스명](){}
  • 생성자는 중복으로 있을 수 있다.

this(), this

이름만 같고 엄연히 다른 애들

생성자 this(): 생성자에서 같은 클래스의 다른 생성자를 호출 할 때 구분지으려고 쓴다. 꼭 첫번째 줄에서 사용. 참조변수 this: lv와 iv의 이름이 같을 때 iv를 구분지으려고 쓴다. 이미 메모리에 올라간 static 메서드에서는 사용 불가능

// 속성(var)
...

  public AppleWatch(){ 
		System.out.println("// Constructor");
	};
// 생성자 오버로딩
	public AppleWatch(int series, String material, String color, int size, String band) {
		this();
		this.series = series;
		this.material = material;
		this.color = color;
		this.size = size;
		this.band = band;
	}

// 기능(method)
...

변수의 초기화

cv, iv는 자동 초기화 lv는 수동으로 초기화 필요

자료형 초기값
byte,short,int 0
long 0L
float 0.0f
double 0.0d / 0.0
boolean false
char ‘\u0000’
참조형 null

자바의 정석 7장 시작

상속(inheritance)

extends

클래스 간에 있어 관계 결정하기: 상속이냐(is-a), 포함이냐(has-a) 모든 클래스는 object.class를 상속받는다

오버라이딩(override)

조상에서 상속받은 메서드를 자손 클래스에 맞게 변경. (=overwrite)

오버라이딩의 조건 - 같은 이름 - 같은 매개변수 - 같은 반환 타입

//기능(method)
...

@Override
	String showLocalTime() {
		return null;
	}
	@Override
	int stopWatch(int initTime) {
		while(true) {
			System.out.println(initTime);
			initTime--;
			if(initTime==0) break;
		}
		return 0;
	}

super, super()

이름만 같고 엄연히 다른 애들

생성자 super():

참조변수 super:

abstract public class Watch {
	String brand;
	int modelNumber;
	public Watch() {System.out.println("\\super()");}
	public Watch(String brand, int modelNumber) { 
		this();
		this.brand = brand;
		this.modelNumber = modelNumber;
		System.out.println("brand = " + brand + " model = " + modelNumber);
		}
	// abstract method
	...
	}

제어자(modifier)

private (default) protected public
같은 클래스 같은 패키지 같은패키지+상속받는클래스 제한없음

클래스에는 public, (default) 만 가능하다. 메서드에는 4개 모두 가능하다.

사용하는 이유 - 외부로부터 데이터를 보호(캡슐화) - 외부로부터 감추기 위해서 예시 - getter, setter - 클래스 내부에서만 쓰이는 validation 메서드

다형성(polymorphism)

여러 형태를 가지는 특성 조상타입 참조변수로 자손타입 인스턴스를 다루는 것 먼저, 역할(인터페이스)과 구현(클래스)을 분리해서 생각해보자.

IT유투버(클라이언트) -> 스마트폰(역할:인터페이스) <… {아이폰, 삼성폰, 엘지폰}(구현:클래스)

  • 클라이언트는 의존 대상의 역할(인터페이스)만 알면 된다.
  • 클라이언트는 구현 대상의 내부 구조를 몰라도 된다.(변경에 영향을 받지 않는다) = 클라이언트의 변경없이 서버의 구현 기능을 유연하게 변경/확장 할 수 있다!

객체 생성

객체 생성을 할 때, 조상타입의 참조변수로 자손타입의 인스턴스를 다룰 수 있지만 그 반대의 경우는 불가능하다. 참조변수와 인스턴스 사이에 멤버 갯수 차이를 생각해서 이해해본다.

형변환(Casting)

Primitive 타입 형변환

8개의 Primitive 타입은 표현할 수 있는 데이터의 크기가 각기 다르다. 물병에 비유를 한다면 큰 물병을 작은 물병으로 옮기면 들어가지 않는 만큼 손실이 일어나고 이는 데이터의 변형에 해당한다.

Reference 타입 형변환

사용할 수 있는 멤버의 갯수를 형변환을 통해 조절한다. 조상-자손 관계에서 서로 형변환 가능하다. Upcasting, Downcasting 구분 말고 형변환 타입을 생략하지 않으면 된다.

instanceof 연산자를 통해 형변환 가능 여부를 알 수 있다.(boolean) 컴파일에서 통과 될 수도 있지만 런타임에서 ClassCastException 터질 수도 있다.

void doWork(Car c){
	if(c instanceof FireEngine){
		FireEngine fe = (FireEngine) c;
		fe.water();	//에러
		...
	}else if(c instanceof Ambulance){
		Ambulance amb = (Ambulance) c;
		amb.siren();	//에러
	}
}

사용하는 이유 - 객체지향의 유연성을 제공 - 다형적 매개변수 - 하나의 배열로 여러 종류의 객체를 다룰 수 있다

추상클래스(abstract)

추상 메서드가 있는 클래스 클래스 안에 추상메서드 단 하나라도 있으면 추상클래스

클래스, 메서드 이름 앞에 abstract 키워드를 갖고 있다. 추상메서드에는 선언부는 있지만, 구현부 { } 가 없다. 추상메서드는 미완성이라 인스턴스 생성 불가. 다른 클래스가 이 추상메서드를 extends 해서 상속한다. 모두 구현하지 않고 일부만 구현하는 경우, 컴파일 에러 발생.

abstract public class Watch {
  //field
  ...
  //constructor
  ...
  // abstract method
	abstract String showLocalTime();
	abstract int stopWatch(int initTime);
}

작성 방법

기존 클래스들에서 공통 부분을 골라서 추상화 한다. 하나의 추상클래스 보다 의미있는 단계별로 여러개 만들어 놓는다. 클래스는 명사, 메서드는 동사

사용하는 이유 - 상속받아 오버라이드할 자손클래스들이 각각 다르게 구현 될 경우 - 클래스들의 변경/관리에 용이

인터페이스(interface)

Implements 추상메서드들의 집합. 구현된 것이 없는 설계도. 모든 멤버가 public인 껍데기.

인터페이스의 조상은 인터페이스만 가능 다중 인터페이스 상속이 가능하다 클래스에서 implements로 구현한다. 추상메서드와 다르게 iv(필드값, 생성자, 인스턴스 메서드)가 없다 인터페이스는 -able

인터페이스를 이용한 다형성

  • 인터페이스를 메서드의 리턴타입으로 지정할 수 있다.
  • 해당 인터페이스를 구현한 클래스의 인스턴스를 리턴
  • 해당 인터페이스를 구현해야 타입 일치가 가능하니까

인터페이스의 default 메서드

새롭게 interface에 메서드를 추가 할 경우, 이미 설계와 구현이 완료된 클래스들의 숫자 만큼 다시 override해서 구현해야한다. 대신에 interface 내에 default 키워드를 가진 iv 메서드를 선언할 수 있다. 예외적인 경우이고 만약 충돌이 나는 경우 개발자가 직접 override하면 된다.

사용하는 이유 - extendsimplements를 통한 다중 상속같은 효과 - 선언과 구현을 분리시킬 수 있다 - 의존관계가 Interface를 바라보게 만들어서 내용이 바뀌어도 코드의 변경이 없다 - interface가 큰 틀이 되어 개발 시간을 단축할 수 있다. - 표준화가 가능하다(ex: JDBC) - 서로 관계없는 클래스들과 인터페이스로 새로운 관계를 만들 수 있다.

내부 클래스(inner class)

클래스 안의 클래스 어차피 밖에서 쓰지도 않을거 같이 캡슐화

내부 클래스는 일반 객체처럼 모든 제어자가 붙을 수 있다. static 내부 클래스 경우 일반 static 클래스 처럼 iv,lv에 접근 불가능. static 내부 클래스만 static 멤버를 정의할 수 있다. 메서드 안에 있는 지역내부클래스의 경우 호출된 메서드보다 더 오래 작업시간이 긴 경우가 있다. 메서드 종료와 함께 소멸되는 지역변수 lv를 지역내부클래스에서 쓸 경우 예외가 생길 수 있다. 그래서 final 키워드를 받는 상수를 쓰는게 안전하다. 상수는 constant pool이라는 곳에서 따로 관리를 받기 때문이다.

내부에 클래스가 있어서 객체를 생성하지 않고도 멤버에 접근이 용이하다. 코드의 복잡성이 줄어든다.

Bill Pugh Singleton Solution

익명 클래스(anonymous class)

이름 없이 정의와 생성을 동시에 하는 일회용 클래스

단 한번만 불리고 더이상 쓰일 경우가 없는 경우에 쓰인다. 파라미터에 new로 객체 생성할 경우에 종종 보인다.

new abstracts조상클래스 이름(){
	// 멤버 선언
}
new inplements인터페이스 이름(){
	// 멤버 선언
}