OOP(객체지향 프로그래밍)
- 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법
- 장점
- 코드 재사용이 용이하다.
- 유지보수가 쉽다.
- 단점
- 처리속도가 상대적으로 느리다.
- 설계시에 많은 시간과 노력이 필요하다.
클래스와 인스턴스(객체)
- 클래스 : 객체의 속성(Attribute)과 행위(Behavior)를 변수와 메소드로 정의한 것
- 객체 : 클래스에 정의된 내용을 바탕으로 실제 메모리에 할당된 데이터
OOP(객체지향 프로그래밍) 4가지 특징(캡상추다)
1. 캡슐화
- 객체의 멤버변수는 private로 설정하여 외부에서 접근을 거부하고, Getter/Setter를 public으로 선언하여 객체의 속성에 접근하도록 사용한다.
- 그 이유는 객체의 무결성을 보장하기 위함인데, 객체 필드에 직접적인 접근을 거부하고 잘못된 입력에 대해 Getter/Setter는 사전에 처리 및 제한할 수 있기 때문이다.
< 접근자 >
- public : 모든 클래스, 패키지에 접근이 가능한 접근 제어자
- private : 같은 패키지에서만 접근 허용, 상속받은 자식 클래스에서는 서로 다른 패키지다 하더라도 접근 가능
- default : 같은 패키지에서만 접근 허용
- protected : 같은 클래스에서만 접근 허용
2. 상속
- 클래스의 멤버와 함수를 다른 클래스에 물려주거나, 물려 받는 것을 말하며 이를 통해 재사용성이 높아진다.
3. 추상화
- 중요한 정보만을 표현함으로써 공통의 속성이나 기능을 묶어 이를 하나의 클래스로 다루는 것
4. 다형성
- 오버로딩과 오버라이딩을 통해 다른 클래스의 객체가 같은 메시지를 받았을 때 각자의 방식으로 동작하는 능력
- 오버로딩(Overloadding) : 상위 클래스의 이름과 return값이 같지만, 매개변수를 다른 메소드를 만들어 가독성을 높이는 방법
- 오버라이딩(Overriding) : 상위 클래스에 존재하는 메소드를 하위 클래스에서 용도에 맞게 재정의 하여 재사용성을 높이는 방법
< Abstract Class와 Interface >
- 추상클래스와 인터페이스는 선언만 있고 구현 내용이 없는 클래스다.
- 인터페이스와 추상클래스를 가지고 새로운 인스턴스(객체)를 생성할 수 없기 때문에 상속을 통해 자식 클래스에서만 객체를 생성할 수 있다.
Abstract Class(추상 클래스)
- 공통점을 찾아 추상화시켜서 사용하는 것으로, extends 키워드를 통해 추상 클래스를 상속받는다.
- 추상클래스는 추상메서드(abstract method)가 하나라도 존재하는 클래스를 말한다.
- 일부는 구현된 메소드도 있을 수 있고, 일부는 abstract method로 구현이 되어있지 않은 메소드도 있을 수 있다.
- 즉, 구현된 메소드가 있을 수 있기 때문에 만들어야할 여러 클래스들의 공통점을 찾아 추상화시켜서 사용한다.
Interface
- 구현 객체가 같은 동작을 한다는 것을 보장하기 위한 목적으로, implements 키워드를 통해 인터페이스를 상속받는다.
- 인터페이스는 쉽게 말하면 껍데기라고 말할 수 있고, 설계도 또는 명세라고 생각하면 된다.
- 모든 메소드가 추상 메소드이기 때문에 인터페이스를 상속받는 자식 클래스는 인터페이스의 모든 메소드를 필수적으로 구현해야 한다.
< static 함수 >
- 메모리에 호출되는데 static 으로 함수 또는 클래스를 선언했을 경우에 컴파일 동시에 메모리에 호출된다.
- 이렇게 호출된 static은 프로그램이 종료되는 시점까지 유지된다.
- Main 함수의 경우 Java에서의 프로그램의 시작과 끝이기 때문에 Static으로 선언되어야 한다.
- 참고) Singletone 패턴
< public static void main의 의미 >
- 모든 클래스들이 접근 가능하여야 하고, 시작되기 전 메모리에 올려져 있어야 하며, return 값에는 의미가 없기 때문에 public static void main를 사용한다.
Call by Value vs Call by Reference
Call by Value (값에 의한 호출) « Java
- 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다.
- 함수 호출시 인자로 전달되는 변수의 값을 복사하여 함수의 인자로 전달한다.
- 복사된 인자는 함수 안에서 지역적으로 사용되는 local value의 특성을 가진다.
- 따라서 함수 안에서 인자의 값이 변경되어도, 외부의 변수의 값은 변경되지 않는다.
Call by Reference (참조에 의한 호출)
- 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다.
- 함수 호출시 인자로 전달되는 변수의 레퍼런스를 전달한다. (해당 변수를 가리킨다.)
- 따라서 함수 안에서 인자의 값이 변경되면, 인자로 전달된 변수의 값도 함께 변경된다.
- cf) Java는 자료형은(Integer, Long, String, …) Call by Value이다. 하지만 배열, 클래스는 Call by Reference로 생각하는 것이 편하다.
< StringBuffer vs StringBuilder >
- StringBuffer : Multi-Thread 환경에서 동기화가 가능하기 때문에 Thread-Safe하다.
- StringBuilder : 동기화를 지원하지 않기 때문에 멀티쓰레드환경에서는 적합하지 않지만 Single-Thread 환경에서 빠르다.
< Annotation >
- 어노테이션이란 본래 주석이란 뜻으로, 인터페이스를 기반으로 한 문법으로 주석처럼 코드에 달아 클래스에 특별한 의미를 부여하거나 기능을 주입할 수 있다.
- 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공 및 실행 시(런타임 시) 특정 기능을 실행하도록 정보를 제공한다.
- 어노테이션은 컴파일러에게 이 소스코드를 어떻게 처리해야 되는 것인지 표시를 해준다.
- 예를들어 내장 어노테이션인 @Override 경우 해당 메소드가 부모클래스를 오버라이딩 한 메소드라고 컴파일러에게 미리 일러주는 것이다.
- 따라서 컴파일러는 런타임 이전에 이 메소드가 문제없이 오버라이딩 되었는지 검사한다.
< Generic >
- 제네릭 타입을 이용해서 컴파일 과정에서 타입 체크를 할 수 있다.
- 제네릭은 클래스와 인터페이스, 메소드를 정의할 때 타입 파라미터로 사용한다.
- 컴파일할 때 타입을 체크해서 에러를 사전에 잡을 수 있다.
- 컴파일러가 타입캐스팅을 해주기 때문에 개발자가 편리하다.
- 타입만 다르고 코드의 내용이 대부분 일치할 때, 코드의 재사용성이 좋아진다.