기술 면접 예상 질문 대비하기 - Java편
Java편
java의 문법과 특징 및 기본적인 개념을 이해하고 기술 면접에 대비하자!
계속해서 추가할 예정입니다!
Do-Hee의 tech-interview - 이 Github 페이지와 동기화되어 있습니다.
java 프로그래밍이란
java와 c/c++의 차이점
- java와 c/c++의 가장 큰 차이점은 실행 환경이다.
- java에서의 개발: 컴파일 혹은 컴파일 + jar압축
- 자바는 링크 과정이 없이 컴파일러가 바로 바이트 코드를 생성
- c/c++에서의 개발: 컴파일 + 링크
java 언어의 장단점
- 장점
- 운영체제에 독립적이다.
- JVM에서 동작하기 때문에, 특정 운영체제에 종속되지 않는다.
- 객체지향 언어이다.
- 객체지향적으로 프로그래밍 하기 위해 여러 언어적 지원을 하고있다. (캡슐화, 상속, 추상화, 다형성 등)
- 객체지향 패러다임의 특성상 비교적 이해하고 배우기 쉽다.
- 자동으로 메모리 관리를 해준다.
- JVM에서 Garbage Collector라고 불리는 데몬 쓰레드에 의해 GC(Garbage Collection)가 일어난다. GC로 인해 별도의 메모리 관리가 필요 없으며 비지니스 로직에 집중할 수 있다. (참고)
- 오픈소스이다.
- 정확히 말하면 OpenJDK가 오픈소스이다. OracleJDK는 사용 목적에 따라서 유료가 될 수 있다.
- OracleJDK의 유료화 이슈는 다음을 참고. (참고)
- 많은 Java 개발자가 존재하고 생태계가 잘 구축되어있다. 덕분에 오픈소스 라이브러리가 풍부하며 잘 활용한다면 짧은 개발 시간 내에 안정적인 애플리케이션을 쉽게 구현할 수 있다.
- 정확히 말하면 OpenJDK가 오픈소스이다. OracleJDK는 사용 목적에 따라서 유료가 될 수 있다.
- 멀티스레드를 쉽게 구현할 수 있다.
- 자바는 스레드 생성 및 제어와 관련된 라이브러리 API를 제공하고 있기 때문에 실행되는 운영체제에 상관없이 멀티 스레드를 쉽게 구현할 수 있다.
- 동적 로딩(Dynamic Loading)을 지원한다
- 애플리케이션이 실행될 때 모든 객체가 생성되지 않고, 각 객체가 필요한 시점에 클래스를 동적 로딩해서 생성한다. 또한 유지보수 시 해당 클래스만 수정하면 되기 때문에 전체 애플리케이션을 다시 컴파일할 필요가 없다. 따라서 유지보수가 쉽고 빠르다.
- 운영체제에 독립적이다.
- 단점
- 비교적 속도가 느리다.
- 자바는 한 번의 컴파일링으로 실행 가능한 기계어가 만들어지지 않고 JVM에 의해 기계어로 번역되고 실행하는 과정을 거치기 때문에 C나 C++의 컴파일 단계에서 만들어지는 완전한 기계어보다는 속도가 느리다. 그러나 하드웨어의 성능 향상과 바이트 코드를 기계어로 변환해주는 JIT 컴파일러 같은 기술 적용으로 JVM의 기능이 향상되어 속도의 격차가 많이 줄어들었다.
- 예외처리가 불편하다.
- 프로그래머 검사가 필요한 예외가 등장한다면 무조건 프로그래머가 선언을 해줘야 한다.
- 비교적 속도가 느리다.
java의 접근 제어자의 종류와 특징
OOP의 4가지 특징
- 추상화
- 구체적인 사물들의 공통적인 특징을 파악해서 이를 하나의 개념(집합)으로 다루는 것
- 캡슐화
- 정보 은닉(information hiding): 필요가 없는 정보는 외부에서 접근하지 못하도록 제한하는 것
- 일반화 관계
- 여러 개체들이 가진 공통된 특성을 부각시켜 하나의 개념이나 법칙으로 성립시키는 과정
- 다형성
- 서로 다른 클래스의 객체가 같은 메시지를 받았을 때 각자의 방식으로 동작하는 능력
OOP의 5대 원칙 (SOLID)
- S: 단일 책임 원칙(SRP, Single Responsibility Principle)
- 객체는 단 하나의 책임만 가져야 한다.
- O: 개방-폐쇄 원칙(OCP, Open Closed Principle)
- 기존의 코드를 변경하지 않으면서 기능을 추가할 수 있도록 설계가 되어야 한다.
- L: 리스코프 치환 원칙(LSP, Liskov Substitution Principle)
- 일반화 관계에 대한 이야기며, 자식 클래스는 최소한 자신의 부모 클래스에서 가능한 행위는 수행할 수 있어야 한다.
- I: 의존 역전 원칙(DIP, Dependency Inversion Principle)
- 의존 관계를 맺을 때 변화하기 쉬운 것 또는 자주 변화하는 것보다는 변화하기 어려운 것, 거의 변화가 없는 것에 의존하라는 것이다.
- D: 인터페이스 분리 원칙(ISP, Interface Segregation Principle)
- 인터페이스를 클라이언트에 특화되도록 분리시키라는 설계 원칙이다.
객체지향 프로그래밍과 절차지향 프로그래밍의 차이
- 절차지향 프로그래밍
- 실행하고자 하는 절차를 정하고, 이 절차대로 프로그래밍하는 방법
- 목적을 달성하기 위한 일의 흐름에 중점을 둔다.
- 객체지향 프로그래밍
- 실세상의 물체를 객체로 표현하고, 이들 사이의 관계, 상호 작용을 프로그램으로 나타낸다.
- 객체를 추출하고 객체들의 관계를 결정하고 이들의 상호 작용에 필요한 함수(메서드)와 변수(필드)를 설계 및 구현하다.
- 객체 지향의 행심은 연관되어 있는 변수와 메서드를 하나의 그룹으로 묶어서 그룹핑하는 것이다.
- 사람의 사고와 가장 비슷하게 프로그래밍을 하기 위해서 생성된 기법
- 하나의 클래스를 바탕으로 서로 다른 상태를 가진 인스턴스를 만들면 서로 다른 행동을 하게 된다. 즉, 하나의 클래스가 여러 개의 인스턴스가 될 수 있다는 점이 객체 지향이 제공하는 가장 기본적인 재활용성이라고 할 수 있다.
객체지향(Object-Oriented)이란
java의 non-static 멤버와 static 멤버의 차이
- non-static 멤버
- 공간적 특성: 멤버는 객체마다 별도로 존재한다.
- 인스턴스 멤버 라고 부른다.
- 시간적 특성: 객체 생성 시에 멤버가 생성된다.
- 객체가 생길 때 멤버도 생성된다.
- 객체 생성 후 멤버 사용이 가능하다.
- 객체가 사라지면 멤버도 사라진다.
- 공유의 특성: 공유되지 않는다.
- 멤버는 객체 내에 각각의 공간을 유지한다.
- 공간적 특성: 멤버는 객체마다 별도로 존재한다.
- static 멤버
- 공간적 특성: 멤버는 클래스당 하나가 생성된다.
- 멤버는 객체 내부가 아닌 별도의 공간에 생성된다.
- 클래스 멤버 라고 부른다.
- 시간적 특성: 클래스 로딩 시에 멤버가 생성된다.
- 객체가 생기기 전에 이미 생성된다.
- 객체가 생기기 전에도 사용이 가능하다. (즉, 객체를 생성하지 않고도 사용할 수 있다.)
- 객체가 사라져도 멤버는 사라지지 않는다.
- 멤버는 프로그램이 종료될 때 사라진다.
- 공유의 특성: 동일한 클래스의 모든 객체들에 의해 공유된다.
- 공간적 특성: 멤버는 클래스당 하나가 생성된다.
java의 final 키워드 (final/finally/finalize)
- final 키워드
- 개념: 변수나 메서드 또는 클래스가 ‘변경 불가능’하도록 만든다.
- 원시(Primitive) 변수에 적용 시
- 해당 변수의 값은 변경이 불가능하다.
- 참조(Reference) 변수에 적용 시
- 참조 변수가 힙(heap) 내의 다른 객체를 가리키도록 변경할 수 없다.
- 메서드에 적용 시
- 해당 메서드를 오버라이드할 수 없다.
- 클래스에 적용 시
- 해당 클래스의 하위 클래스를 정의할 수 없다.
- finally 키워드
- 개념: try/catch 블록이 종료될 때 항상 실행될 코드 블록을 정의하기 위해 사용한다.
- finally는 선택적으로 try 혹은 catch 블록 뒤에 정의할 때 사용한다.
- finally 블록은 예외가 발생하더라도 항상 실행된다.
- 단, JVM이 try 블록 실행 중에 종료되는 경우는 제외한다.
- finally 블록은 종종 뒷마무리 코드를 작성하는 데 사용된다.
- finally 블록은 try와 catch 블록 다음과, 통제권이 이전으로 다시 돌아가기 전 사이에 실행된다.
- finalize() 메서드
- 개념: 쓰레기 수집기(GC, Garbage Collector)가 더 이상의 참조가 존재하지 않는 객체를 메모리에서 삭제하겠다고 결정하는 순간 호출된다.
- Object 클래스의 finalize() 메서드를 오버라이드해서 맞춤별 GC를 정의할 수 있다.
protected void finalize() throws Throwable { // 파일 닫기, 자원 반환 등등 }
java의 제네릭(Generic)과 c++의 템플릿(Template)의 차이
- java의 제네릭(Generic)
- 개념: 모든 종류의 타입을 다룰 수 있도록 일반화된 타입 매개 변수(generic type)로 클래스나 메서드를 선언하는 기법
- 처리 방법: 타입 제거(type erasure)라는 개념에 근거한다.
- 소스 코드를 JVM이 인식하는 바이트 코드로 변환할 때 인자로 주어진 타입을 제거하는 기술이다.
- 제네릭이 있다고 해서 크게 달라지는 것은 없다. 단지 코드를 좀 더 예쁘게 할 뿐이다.
- 그래서 java의 제네릭(Generic)은 때로는 문법적 양념(syntactic sugar) 이라고 부른다.
Vector<String> vector = new Vector<String>(); vector.add(new String("hello")); String str = vector.get(0); // 컴파일러가 아래와 같이 변환 Vector vector = new Vector(); vector.add(new String("hello")); String str = (String) vector.get(0);
- c++의 템플릿(Template)
- 개념: 템플릿은 하나의 클래스를 서로 다른 여러 타입에 재사용할 수 있도록 하는 방법
- 예를 들어 여러 타입의 객체를 저장할 수 있는 연결리스트와 같은 자료구조를 만들 수 있다.
- 처리 방법: 컴파일러는 인자로 주어진 각각의 타입에 대해 별도의 템플릿 코드를 생성한다.
- 예를 들어 MyClass
가 MyClass 와 정적 변수(static variable)를 공유하지 않는다. - 하지만 java에서 정적 변수는 제네릭(Generic) 인자로 어떤 타입을 주었는지에 관계없이 MyClass로 만든 모든 객체가 공유한다.
- 즉, 템플릿은 좀 더 우아한 형태의 매크로 다.
/** MyClass.h **/ template<class T> class MyClass { public: static int val; MyClass(int v) { val = v; } }; /** MyClass.cpp **/ template<typename T> int MyClass<T>::bar; template class MyClass<Foo>; template class MyClass<Bar>; /** main.cpp **/ MyClass<Foo> * foo1 = new MyClass<Foo>(10); MyClass<Foo> * foo2 = new MyClass<Foo>(15); MyClass<Bar> * bar1 = new MyClass<Bar>(20); MyClass<Bar> * bar2 = new MyClass<Bar>(35); int f1 = foo1->val; // 15 int f2 = foo2->val; // 15 int b1 = bar1->val; // 35 int b2 = bar2->val; // 35
- 예를 들어 MyClass
- 개념: 템플릿은 하나의 클래스를 서로 다른 여러 타입에 재사용할 수 있도록 하는 방법
- java의 제네릭과 c++의 템플릿의 차이
- List
처럼 코드를 작성할 수 있다는 이유에서 동등한 개념으로 착각하기 쉽지만 두 언어가 이를 처리하는 방법은 아주 많이 다르다. - c++의 Template에는 int와 같은 기본 타입을 인자로 넘길 수 있지만, java의 Generic에서는 Integer을 대신 사용해야 한다.
- c++의 Template은 인자로 주어진 타입으로부터 객체를 만들어 낼 수 있지만, java에서는 불가능하다.
- java에서 MyClass로 만든 모든 객체는 Generic 타입 인자가 무엇이냐에 관계없이 전부 동등한 타입이다.(실행 시간에 타입 인자 정보는 삭제된다.)
- c++에서는 다른 Template 타입 인자를 사용해 만든 객체는 서로 다른 타입의 객체이다.
- java의 경우 Generic 타입 인자를 특정한 타입이 되도록 제한할 수 있다.
- 예를 들어 CardDeck을 Generic 클래스로 정의할 때 CardGame의 하위 클래스만 사용되도록 제한할 수 있다.
- java에서 Generic 타입의 인자는 정적 메서드나 변수를 선언하는 데 사용될 수 없다.
- 왜냐하면 MyClass
나 MyClass 가 이 메서드와 변수를 공유하기 때문이다. - c++ Template은 이 두 클래스를 다른 클래스로 처리하므로 Template 타입 인자를 정적 메서드나 변수를 선언하는 데 사용할 수 있다.
- 왜냐하면 MyClass
- List
java의 가비지 컬렉션(Garbage Collection) 처리 방법
객체 직렬화(Serialization)와 역직렬화(Deserialization)란 무엇인가
클래스, 객체, 인스턴스의 차이
- 클래스(Class)
- 객체를 만들어 내기 위한 설계도 혹은 틀
- 연관되어 있는 변수와 메서드의 집합
- 객체(Object)
- 소프트웨어 세계에 구현할 대상
- 클래스에 선언된 모양 그대로 생성된 실체
<!– * 일반적으로 설계도인 클래스가 구체적인 실체인 인스턴스(인스턴스화)가 되었을 때 ‘객체’라고 부른다.
- 즉, 메모리에 할당된 실체화된 인스턴스를 ‘객체’라고 부른다. –>
- ‘클래스의 인스턴스(instance)’ 라고도 부른다.
- 객체는 모든 인스턴스를 대표하는 포괄적인 의미를 갖는다.
- oop의 관점에서 클래스의 타입으로 선언되었을 때 ‘객체’라고 부른다.
- 인스턴스(Instance)
- 설계도를 바탕으로 소프트웨어 세계에 구현된 구체적인 실체
- 즉, 객체를 소프트웨어에 실체화 하면 그것을 ‘인스턴스’라고 부른다.
- 실체화된 인스턴스는 메모리에 할당된다.
- 인스턴스는 객체에 포함된다고 볼 수 있다.
- oop의 관점에서 객체가 메모리에 할당되어 실제 사용될 때 ‘인스턴스’라고 부른다.
- 추상적인 개념(또는 명세)과 구체적인 객체 사이의 관계 에 초점을 맞출 경우에 사용한다.
- ‘~의 인스턴스’ 의 형태로 사용된다.
- 객체는 클래스의 인스턴스다.
- 객체 간의 링크는 클래스 간의 연관 관계의 인스턴스다.
- 실행 프로세스는 프로그램의 인스턴스다.
- 즉, 인스턴스라는 용어는 반드시 클래스와 객체 사이의 관계로 한정지어서 사용할 필요는 없다.
- 인스턴스는 어떤 원본(추상적인 개념)으로부터 ‘생성된 복제본’을 의미한다.
/* 클래스 */ public class Animal { ... } /* 객체와 인스턴스 */ public class Main { public static void main(String[] args) { Animal cat, dog; // '객체' // 인스턴스화 cat = new Animal(); // cat은 Animal 클래스의 '인스턴스'(객체를 메모리에 할당) dog = new Animal(); // dog은 Animal 클래스의 '인스턴스'(객체를 메모리에 할당) } }
- 설계도를 바탕으로 소프트웨어 세계에 구현된 구체적인 실체
- Q. 클래스 VS 객체
- 클래스는 ‘설계도’, 객체는 ‘설계도로 구현한 모든 대상’을 의미한다.
- Q. 객체 VS 인스턴스
- 클래스의 타입으로 선언되었을 때 객체라고 부르고, 그 객체가 메모리에 할당되어 실제 사용될 때 인스턴스라고 부른다.
- 객체는 현실 세계에 가깝고, 인스턴스는 소프트웨어 세계에 가깝다.
- 객체는 ‘실체’, 인스턴스는 ‘관계’에 초점을 맞춘다.
- 객체를 ‘클래스의 인스턴스’라고도 부른다.
- ‘방금 인스턴스화하여 레퍼런스를 할당한’ 객체를 인스턴스라고 말하지만, 이는 원본(추상적인 개념)으로부터 생성되었다는 것에 의미를 부여하는 것일 뿐 엄격하게 객체와 인스턴스를 나누긴 어렵다.
- 추상화 기법
- 분류(Classification)
- 객체 -> 클래스
- 실재하는 객체들을 공통적인 속성을 공유하는 범부 또는 추상적인 개념으로 묶는 것
- 인스턴스화(Instantiation)
- 클래스 -> 인스턴스
- 분류의 반대 개념. 범주나 개념으로부터 실재하는 객체를 만드는 과정
- 구체적으로 클래스 내의 객체에 대해 특정한 변형을 정의하고, 이름을 붙인 다음, 그것을 물리적인 어떤 장소에 위치시키는 등의 작업을 통해 인스턴스를 만드는 것을 말한다.
- ‘예시(Exemplification)’라고도 부른다.
- 분류(Classification)
객체(Object)란 무엇인가
오버로딩과 오버라이딩의 차이(Overloading vs Overriding)
- 오버로딩(Overloading)
- 두 메서드가 같은 이름을 갖고 있으나 인자의 수나 자료형이 다른 경우
- Ex)
public double computeArea(Circle c) { ... }
public double computeArea(Circle c1, Circle c2) { ... }
public double computeArea(Square c) { ... }
- 오버라이딩(Overriding)
- 상위 클래스의 메서드와 이름과 용례(signature)가 같은 함수를 하위 클래스에 재정의하는 것
- 상속 관계에 있는 클래스 간에 같은 이름의 메서드를 정의
- Ex) Circle에서 printMe() 메서드를 재정의한다.
public abstract class Shape { public void printMe() { System.out.println("Shape"); } public abstract double computeArea(); } public class Circle extends Shape { private double rad = 5; @Override // 개발자의 실수를 방지하기 위해 @Override(annotation) 쓰는 것을 권장 public void printMe() { System.out.println("Circle"); } public double computeArea() { return rad * rad * 3.15; } } public class Ambiguous extends Shape { private double area = 10; public double computeArea() { return area; } }
Call by Reference와 Call by Value의 차이
- Call by Value (값에 의한 호출)
- 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다.
- 함수 호출시 인자로 전달되는 변수의 값을 복사하여 함수의 인자로 전달한다.
- 복사된 인자는 함수 안에서 지역적으로 사용되는 local value의 특성을 가진다.
- 따라서 함수 안에서 인자의 값이 변경되어도, 외부의 변수의 값은 변경되지 않는다.
- Call by Reference (참조에 의한 호출)
- 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다.
- 함수 호출시 인자로 전달되는 변수의 레퍼런스를 전달한다. (해당 변수를 가르킨다.)
- 따라서 함수 안에서 인자의 값이 변경되면, 인자로 전달된 변수의 값도 함께 변경된다.
- Java는 Call by Value 일까? Call by Reference 일까?
class Person { private String name; public Person(String name) { this.name = name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "name is " + this.name; } }
public class FunctionCallTest { public static void assignNewPerson(Person p) { p = new Person("hee"); } public static void changeName(Person p) { p.setNaem("hee"); } public static void main(String[] args) { Person p = new Person("doy"); assignNewPerson(p); System.out.println(p); // name is doy changeName(p); System.out.println(p); // name is hee } }
- 기본자료형은 Call By Value이고, 참조자료형은 Call By Reference이다??
- 오해 1. 특정 메서드 내에서 전달 받은 객체의 상태를 변경 할 수 있다.
changeName
메서드는 참조변수 p가 가리키는 [이름 속성이 “doy”인 Person 객체]를 [이름 속성이 “hee”인 새로운 Person 객체]로 변경한 것이 아니라, 단지 이름 속성만 변경했을 뿐이다.
- 오해 2. 참조변수는 임의의 객체에 대한 레퍼런스를 저장하므로 메서드로 전달한 값이 레퍼런스(Call by Reference)이다.
- 전달 된 레퍼런스는 참조변수 p 자체의 레퍼런스가 아닌 p가 저장하고 있는 값(이것도 레퍼런스)이다.
- 만약 Java가 Call by Reference를 지원한다면
assignNewPerson
메서드 실행 후에 p 참조변수가 가리키는 객체가 [이름 속성이 “hee”인 새로운 Person 객체]로 변경되어야 한다. - 또한 참조변수 p 자체의 레퍼런스를 얻을 수 있는 방법이 있어야 한다. 그러나 Java는 이 방법을 지원하지 않는다.
- 따라서 Java는 항상 Call by Value 이다.
- 여기서 value 란?
- 기본자료형의 값 또는 객체에 대한 레퍼런스
- 여기서 value 란?
- 기본자료형의 경우 해당하는 변수의 값을 복사해서 전달한다.
- 참조자료형의 경우 해당하는 변수가 가지는 값이 레퍼런스이므로 인자로 넘길 때 Call by Value에 의해 변수가 가지고 있는 레퍼런스가 복사되어 전달된다.
인터페이스와 추상 클래스의 차이(Interface vs Abstract Class)
- 추상 메서드(Abstract Method)
- abstract 키워드와 함께 원형만 선언되고, 코드는 작성되지 않은 메서드
public abstract String getName(); // 추상 메서드 public abstract String fail() { return "Fail"; } // 추상 메서드 아님. 컴파일 오류 발생
- abstract 키워드와 함께 원형만 선언되고, 코드는 작성되지 않은 메서드
- 추상 클래스(Abstract Class)
- 개념: abstract 키워드로 선언된 클래스
- 추상 메서드를 최소 한 개 이상 가지고 abstract로 선언된 클래스
- 최소 한 개의 추상 메서드를 포함하는 경우 반드시 추상 클래스로 선언하여야 한다.
- 추상 메서드가 없어도 abstract로 선언한 클래스
- 그러나 추상 메서드가 하나도 없는 경우라도 추상 클래스로 선언할 수 있다.
- 추상 메서드를 최소 한 개 이상 가지고 abstract로 선언된 클래스
- 추상 클래스의 구현
- 서브 클래스에서 슈퍼 클래스의 모든 추상 메서드를 오버라이딩하여 실행가능한 코드로 구현한다.
- 추상 클래스의 목적
- 객체(인스턴스)를 생성하기 위함이 아니며, 상속을 위한 부모 클래스로 활용하기 위한 것이다.
- 여러 클래스들의 공통된 부분을 추상화(추상 메서드) 하여 상속받는 클래스에게 구현을 강제화하기 위한 것이다. (메서드의 동작을 구현하는 자식 클래스로 책임을 위임)
- 즉, 추상 클래스의 추상 메서드를 자식 클래스가 구체화하여 그 기능을 확장하는 데 목적이 있다.
/* 개념 a의 예시 */ abstract class Shape { // 추상 클래스 Shape() {...} void edit() {...} abstract public void draw(); // 추상 메서드 }
/* 개념 b의 예시 */ abstract class Shape { // 추상 클래스 Shape() {...} void edit() {...} }
/* 추상 클래스의 구현 */ class Circle extends Shape { public void draw() { System.out.println("Circle"); } // 추상 메서드 (오버라이딩) void show() { System.out.println("동그라미 모양"); } }
- 개념: abstract 키워드로 선언된 클래스
- 인터페이스(Interface)
- 개념: 추상 메서드와 상수만을 포함하며, interface 키워드를 사용하여 선언한다.
- 인터페이스의 구현
- 인터페이스를 상속받고, 추상 메서드를 모두 구현한 클래스를 작성한다.
- implements 키워드를 사용하여 구현한다.
- 인터페이스의 목적
- 상속받을 서브 클래스에게 구현할 메서드들의 원형을 모두 알려주어, 클래스가 자신의 목적에 맞게 메서드를 구현하도록 하는 것이다.
- 구현 객체의 같은 동작을 보장하기 위한 목적이 있다.
- 즉, 서로 관련이 없는 클래스에서 공통적으로 사용하는 방식이 필요하지만 기능을 각각 구현할 필요가 있는 경우에 사용한다.
- 인터페이스의 특징
- 인터페이스는 상수 필드와 추상 메서드만으로 구성된다.
- 모든 메서드는 추상 메서드로서, abstract public 속성이며 생략 가능하다.
- 상수는 public static final 속성이며, 생략하여 선언할 수 있다.
- 인터페이스를 상속받아 새로운 인터페이스를 만들 수 있다.
interface MobilePhone extends Phone { }
/* 인터페이스의 개념 */ interface Phone { // 인터페이스 int BUTTONS = 20; // 상수 필드 (public static final int BUTTONS = 20;과 동일) void sendCall(); // 추상 메서드 (abstract public void sendCall();과 동일) abstract public void receiveCall(); // 추상 메서드 }
/* 인터페이스의 구현 */ class FeaturePhone implements Phone { // Phone의 모든 추상 메서드를 구현한다. public void sendCall() {...} public void receiveCall() {...} // 추가적으로 다른 메서드를 작성할 수 있다. public int getButtons() {...} }
- 추상 클래스와 인터페이스의 공통점
- 인스턴스(객체)는 생성할 수 없다.
- 선언만 있고 구현 내용이 없다.
- 자식 클래스가 메서드의 구체적인 동작을 구현하도록 책임을 위임한다.
- 추상 클래스와 인터페이스의 차이점
- 서로 다른 목적을 가지고 있다.
- 추상 클래스는 추상 메서드를 자식 클래스가 구체화하여 그 기능을 확장하는 데 목적이 있다. (상속을 위한 부모 클래스)
- 인터페이스는 서로 관련이 없는 클래스에서 공통적으로 사용하는 방식이 필요하지만 기능을 각각 구현할 필요가 있는 경우에 사용한다. (구현 객체의 같은 동작을 보장)
- 추상 클래스는 클래스이지만 인터페이스는 클래스가 아니다.
- 추상 클래스는 단일 상속이지만 인터페이스는 다중 상속이 가능하다.
- 추상 클래스는 “is a kind of” 인터페이스는 “can do this”
- Ex) 추상 클래스: Appliances(Abstract Class) - TV, Refrigerator
- Ex) 인터페이스: Flyable(Interface) - Plane, Bird
- 서로 다른 목적을 가지고 있다.
JVM 구조
Java Collections Framework
- Map
- 검색할 수 있는 인터페이스
- 데이터를 삽입할 때 Key와 Value의 형태로 삽입되며, Key를 이용해서 Value를 얻을 수 있다.
- Collection
- List
- 순서가 있는 Collection
- 데이터를 중복해서 포함할 수 있다.
- Set
- 집합적인 개념의 Collection
- 순서의 의미가 없다.
- 데이터를 중복해서 포함할 수 없다.
- List
- Collections Framework 선택 과정
- Map과 Collection 인터페이스 중 선택
- 1-1. Collection 선택 시 사용 목적에 따라 List와 Set중 선택
- 사용 목적에 따라 Map, List, Set 각각의 하위 구현체를 선택
- 2-1. Map: HashMap, LinkedHashMap, HashTable, TreeMap
- 2-2. List: LinkedList, ArrayList
- 2-3. Set: TreeSet, HashSet
- Map과 Collection 인터페이스 중 선택
java Map 인터페이스 구현체의 종류
- HashMap
- Entry<K,V>의 배열로 저장되며, 배열의 index는 내부 해쉬 함수를 통해 계산된다.
- 내부 hash값에 따라서 키순서가 정해지므로 특정 규칙없이 출력된다.
- key와 value에 null값을 허용한다.
- 비동기 처리
- 시간복잡도: O(1)
- LinkedHashMap
- HaspMap을 상속받으며, Linked List로 저장된다.
- 입력 순서대로 출력된다.
- 비동기 처리
- 시간복잡도: O(n)
- TreeMap
- 내부적으로 레드-블랙 트리(Red-Black tree)로 저장된다.
- 키값이 기본적으로 오름차순 정렬되어 출력된다.
- 키값에 대한 Compartor 구현으로 정렬 방법을 지정할 수 있다.
- 시간복잡도: O(logn)
- ConCurrentHashMap
- multiple lock
- update할 때만 동기 처리
- key와 value에 null값을 허용하지 않는다.
- HashTable
- single lock
- 모든 메서드에 대해 동기 처리
- key와 value에 null값을 허용하지 않는다.
java Set 인터페이스 구현체의 종류
- HashSet
- 저장 순서를 유지하지 않는 데이터의 집합이다.
- 해시 알고리즘(hash algorithm)을 사용하여 검색 속도가 매우 빠르다.
- 내부적으로 HashMap 인스턴스를 이용하여 요소를 저장한다.
- LinkedHashSet
- 저장 순서를 유지하는 HashSet
- TreeSet
- 데이터가 정렬된 상태로 저장되는 이진 탐색 트리(binary search tree)의 형태로 요소를 저장한다.
- 이진 탐색 트리 중에 성능을 향상시킨 레드-블랙 트리(Red-Black tree)로 구현되어 있다.
- Compartor 구현으로 정렬 방법을 지정할 수 있다.
:java List 인터페이스 구현체의 종류
- ArrayList
- 단방향 포인터 구조로 각 데이터에 대한 인덱스를 가지고 있어 데이터 검색에 적합하다.
- 데이터의 삽입, 삭제 시 해당 데이터 이후 모든 데이터가 복사되므로 삽입, 삭제가 빈번한 데이터에는 부적합하다.
- LinkedList
- 양방향 포인터 구조로 데이터의 삽입, 삭제 시 해당 노드의 주소지만 바꾸면 되므로 삽입, 삭제가 빈번한 데이터에 적합하다.
- 데이터의 검색 시 처음부터 노드를 순회하므로 검색에는 부적합하다.
- 스택, 큐, 양방향 큐 등을 만들기 위한 용도로 쓰인다.
- Vector
- 내부에서 자동으로 동기화 처리가 일어난다.
- 성능이 좋지 않고 무거워 잘 쓰이지 않는다.
Wrapper class
Annotation
String, StringBuilder, StringBuffer
- String
- 새로운 값을 할당할 때마다 새로 클래스에 대한 객체가 생성된다.
- String에서 저장되는 문자열은 private final char[]의 형태이기 때문에 String 값은 바꿀수 없다.
- private: 외부에서 접근 불가
- final: 초기값 변경 불가
- String + String + String…
- 각각의 String 주솟값이 Stack에 쌓이고, Garbage Collector가 호출되기 전까지 생성된 String 객체들은 Heap에 쌓이기 때문에 메모리 관리에 치명적이다.
- String을 직접 더하는 것보다는 StringBuffer나 StringBuilder를 사용하는 것이 좋다.
- StringBuilder, StringBuffer
- memory에 append하는 방식으로, 클래스에 대한 객체를 직접 생성하지 않는다.
- StringBuilder
- 변경가능한 문자열
- 비동기 처리
- StringBuffer
- 변경가능한 문자열
- 동기 처리
- multiple thread 환경에서 안전한 클래스(thread safe)
동기화와 비동기화의 차이(Syncronous vs Asyncronous)
java에서 ‘==’와 ‘Equals()’의 차이
java의 리플렉션(Reflection) 이란
- 리플렉션(Reflection) 이란?
- 자바에서 이미 로딩이 완료된 클래스에서 또 다른 클래스를 동적으로 로딩(Dynamic Loading)하여 생성자(Constructor), 멤버 필드(Member Variables) 그리고 멤버 메서드(Member Method) 등을 사용할 수 있는 기법이다.
- 클래스의 패키지 정보, 접근 지정자, 수퍼 클래스, 어노테이션(Annotation) 등을 얻을 수 있다.
- 컴파일 시간(Compile Time)이 아니라 실행 시간(Run Time)에 동적으로 특정 클래스의 정보를 객체화를 통해 분석 및 추출해낼 수 있는 프로그래밍 기법이다.
- 사용 방법
Class.forName("클래스이름")
- 클래스의 이름으로부터 인스턴스를 생성할 수 있고, 이를 이용하여 클래스의 정보를 가져올 수 있다.
public class DoHee { public String name; public int number; public void setDoHee (String name, int number) { this.name = name; this.number = number; } public void setNumber(int number) { this.number = number; } public void sayHello(String name) { System.out.println("Hello, " + name); } }
import java.lang.reflect.Method; import java.lang.reflect.Field; /* ReflectionTest 클래스 */ public class ReflectionTest { public void reflectionTest() { try { Class myClass = Class.forName("DoHee"); Method[] methods = myClass.getDeclaredMethods(); /* 클래스 내 선언된 메서드의 목록 출력 */ /* 출력 : public void DoHee.setDoHee(java.lang.String,int) public void DoHee.setNumber(int) public void DoHee.sayHello(java.lang.String) */ for (Method method : methods) { System.out.println(method.toString()); } /* 메서드의 매개변수와 반환 타입 확인 */ /* 출력 : Class Name : class DoHee Method Name : setDoHee Return Type : void */ Method method = methods[0]; System.out.println("Class Name : " + method.getDeclaringClass()); System.out.println("Method Name : " + method.getName()); System.out.println("Return Type : " + method.getReturnType()); /* 출력 : Param Type : class java.lang.String Param Type : int */ Class[] paramTypes = method.getParameterTypes(); for(Class paramType : paramTypes) { System.out.println("Param Type : " + paramType); } /* 메서드 이름으로 호출 */ Method sayHelloMethod = myClass.getMethod("sayHello", String.class); sayHelloMethod.invoke(myClass.newInstance(), new String("DoHee")); // 출력 : Hello, DoHee /* 다른 클래스의 멤버 필드의 값 수정 */ Field field = myClass.getField("number"); DoHee obj = (DoHee) myClass.newInstance(); obj.setNumber(5); System.out.println("Before Number : " + field.get(obj)); // 출력 : Before Number : 5 field.set(obj, 10); System.out.println("After Number : " + field.get(obj)); // 출력 : After Number : 10 } catch (Exception e) { // Exception Handling } } public static void main(String[] args) { new ReflectionTest().reflectionTest(); } }
- 왜 사용할까?
- 실행 시간에 다른 클래스를 동적으로 로딩하여 접근할 때
- 클래스와 멤버 필드 그리고 메서드 등에 관한 정보를 얻어야할 때
- 리플렉션 없이도 완성도 높은 코드를 구현할 수 있지만 사용한다면 조금 더 유연한 코드를 만들 수 있다.
- 주의할 점
- 외부에 공개되지 않는 private 멤버도
Field.setAccessible()
메서드를 true로 지정하면 접근과 조작이 가능하기 때문에 주의해야 한다.
- 외부에 공개되지 않는 private 멤버도
관련된 Post
- 알고리즘: 기술 면접 예상 질문 대비하기 - 알고리즘(Algorithm)편 을 참고하시기 바랍니다.
- 데이터베이스: 기술 면접 예상 질문 대비하기 - 데이터베이스(DB)편 을 참고하시기 바랍니다.
- 디자인패턴: 기술 면접 예상 질문 대비하기 - 디자인패턴(Design Pattern)편 을 참고하시기 바랍니다.
- Java: 기술 면접 예상 질문 대비하기 - Java편 을 참고하시기 바랍니다.
- 운영체제: 기술 면접 예상 질문 대비하기 - 운영체제(OS)편 을 참고하시기 바랍니다.