본문 바로가기

언어/Java

Java 경력 기술 면접 준비(1)

객체 지향

Object-Oriented Programming (OOP). 프로그램 설계방법론이자 개념의 일종. 프로그램을 단순히 데이터와 처리 방법으로 나누는 것이 아니라, 프로그램을 수많은 '객체'라는 기본 단위로 나누고 이 객체들의 상호작용으로 서술하는 방식이다. 객체란 하나의 역할을 수행하는 '메소드와 변수(데이터)'의 묶음으로 봐야 한다. [1]

캡슐화(Encapsulation)

변수와 함수를 하나의 단위로 묶는 것을 의미한다. 즉, 데이터의 번들링(Bundling)이다. 대개 프로그래밍 언어에서 이 번들링은 클래스를 통해 구현되고, 해당 클래스의 인스턴스 생성을 통해 클래스 안에 포함된 멤버 변수와 메소드에 쉽게 접근할 수 있다. 클래스는 객체 지향 프로그래밍을 지원하는 거의 대부분의 언어가 제공하는 제1요소이다. [1]

상속(Inheritance)

상속은 자식 클래스가 부모 클래스의 특성과 기능을 그대로 물려받는 것을 말한다. 기능의 일부분을 변경해야 할 경우 자식 클래스에서 상속받은 그 기능만을 수정해서 다시 정의하게 되는데, 이러한 작업을 '오버라이딩(Overriding)'이라고 한다. 상속은 캡슐화를 유지하면서도 클래스의 재사용이 용이하도록 해 준다. [1]

다형성(Polymorphism)

하나의 변수명, 함수명 등이 상황에 따라 다른 의미로 해석될 수 있는 것을 말한다. [1] [2]

  • 제네릭(Generic)
    • 지정한 타입 매개변수에 해당하는 타입만을 사용하겠다고 약속하는 방식이다. 타입 매개변수가 특정 객체를 상속할 경우 상속하는 객체의 함수는 호출할 수 있지만 그렇지 않을 경우 타입 매개변수로 지정된 객체의 멤버에는 접근할 수 없다.
  • 함수 오버로딩(Function overloading)
    • 함수 오버로딩을 통해 동일한 이름의 함수를 매개변수에 따라 다른 기능으로 동작하도록 할 수 있다.
  • 오버라이딩(Overriding)
    • 슈퍼 클래스의 메소드등을 새롭게 정의합니다.
  • 형 변환(Type coercion)
    • 'double a = 30;'이라는 식이 실행되면 int형 값 30은 double로 묵시적 형 변환이 이루어진다. double은 int보다 크기가 큰 자료형이므로, 이러한 형 변환을 자료형 승급(Type promotion)이라고 한다.

메모리 영역

기본적으로 아래와 같이 구분된다.

  • Class Area(Method Area)
  • Stack Area
  • Heap Area
  • Native Method Stack Area
  • PC Register

크게는 아래와 같이 구분한다.

  • Method Area
  • Stack Area
  • Heap Area

Method Area / Class Area

JVM으로 실행시 클래스 파일(.class)의 바이트 코드가 로드되는 영역이다. 모든 스레드가 해당 영역을 공유한다.

Stack Area

Stack 영역은 아래와 같은 특징을 가진다.

  • 각 Thread 는 자신만의 stack 영역을 가진다.
  • pimary 타입의 데이터가 값과 함께 할당된다.
  • 지역 변수 들은 scope 에 따른 visibility 를 가진다.
  • Heap 영역에 생성된 Object 타입의 데이터의 참조값이 할당된다. [3]

Heap Area

  • 애플리케이션의 모든 메모리 중 stack 에 있는 데이터를 제외한 부분이라고 보면 된다.
  • 모든 Object 타입(Integer, String, ArrayList, ...)은 heap 영역에 생성된다. (그리고 참조값이 stack에 저장된다.)
  • 몇개의 스레드가 존재하든 상관없이 단 하나의 heap 영역만 존재한다. [3]

Interface and Abstract Class

추상 클래스와 인터페이스는 상속 받는 클래스(구현 오브젝트)에서 추상 메소드를 구현하도록 강제한다. 다만 추상 클래스 경우 상속 받아서 기능을 사용하고 기능을 확장을 하기 위함이고, 인터페이스는 기능 구현을 강제하기 위함입니다.

부족하지만 구분을 하기 위해서 아래 작은 예제를 만들어보겠습니다.

public class Main {

    public static void main(String[] args) {
        System.out.println("Hello World!");

        final Car car = new Hyundai();
        car.goToOffice();

        final Car secondCar = new Kia();
        secondCar.goToOffice();
    }

    public interface Engine {
        void start();
        void stop();
    }

    public interface Wheel {
        int size();
        void run();
    }

    public static abstract class Car implements Engine, Wheel {

        @Override
        public void start() {
            System.out.println("Engine Start");
        }

        @Override
        public void stop() {
            System.out.println("Engine Stop");
        }

        @Override
        public void run() {
            System.out.println("Wheel Stop");
        }

        // 차 이름을 각 객체에서 구현하도록 한다.
        public abstract String name();

        public void goToOffice() {
            // engine start
            start();

            // run the wheel
            run();

            // stop engine
            stop();
        }
    }

    public static class Hyundai extends Car {

        // 휠의 사이즈는 차별로 다를수 있으니 각 차 객체에서 구현하도록 한다.
        @Override
        public int size() {
            return 100;
        }

        @Override
        public String name() {
            return "현대";
        }
    }

    public static class Kia extends Car {

        @Override
        public int size() {
            return 11;
        }


        @Override
        public String name() {
            return "기아";
        }

        // 기아에서 획기적인 기술로 engine start 하는 방식이 변경된 경우 아래와 같이 구현 할 수 있다.
        @Override
        public void start() {
            System.out.println("New Engine Start");
        }
    }

Reference