LUMI_dev

Java 기술 면접 예상 질문 개념 정리 (지속 업데이트 예정) 본문

JAVA

Java 기술 면접 예상 질문 개념 정리 (지속 업데이트 예정)

luminous_dev 2025. 1. 26. 19:46

Java의 특징

객체 지향, 캡상추다, JVM, Garbage Collector

  • 객체 지향적 프로그래밍 언어 : 자동차 부품 조립하듯 새로운 프로그램 만드는 것
  • 캡슐화, 상속, 다형성 잘 적용됨

 

장점

  • JVM (자바 가상 머신)위에서 작동하기 때문에 운영 체제에 독립적
  •  Garbage Collector을 통한 자동적인 메모리 관리 가능
  • 멀티 쓰레드 지원 

 

  • 오픈소스 라이브러리 풍부함 
  • 이식성 높음 (맥,윈도우 등 여러 OS에서 사용 가능) 

 

단점

  • JVM 위에서 동작하기 때문에 실행 속도가 느림
  • 다중 상속이나 타입에 엄격, 제약 많음 

JVM (자바 가상 머신)

  • 자바 프로그램을 실행하기 위한 가상 환경
  • 컴파일된 바이트 코드를 기계가 이해할 수 있는 기계어로 변환
  • 가비지 컬렉션을 통해  자동적인 메모리 관리 해줌

JVM의 구조와 작동 원리 

 

 

 

구조

Class Loader, Runtime data areas, Execution Engine, GC로 나눠져 있음 

Class Loader 클래스 파일을 Runtime Data Area의 메서드 영역으로 불러옴
Runtime data areas 프로그램을 수행하기 위해 OS로부터 할당받은 메모리 영역 (Java 메모리 공간) 
Execution Engine 클래스를 실행시키는 역할 
같은 ByteCode를 실행 가능하도록 해석함 
Garbage Collector 메모리 관리 기법 중 하나 / Heap 영역에 배치된 객체들을 관리하는 모듈

 

 

 

 

 

 

JVM의 클래스는 언제 로딩되는가?

JVM 클래스는 Lazy Loading 방식으로 필요할 때 로딩하여 메모리 효율성 높임

 

기본적인 클래스 로딩 시점

    1. new 키워드 사용하여 객체 생성할 때
    2. static 변수나 메서드를 참조할 때 
    3. Class.forName("클래스명") 호출할 때
    4. 자식 클래스가 로딩될 때 부모 클래스가 먼저 로딩됨

특수한 경우의 클래스 로딩 

  1. static final 키워드 상수
    • 클래스 로드 되지 않고 사용 가능
    • 컴파일 시점에 값이 확정되기 때문 
    • JVM의 Method Area의 Constant Pool에 따로 저장되어 관리되기 때문 
  2. final 키워드 상수 
    • 클래스 로드 필요 
    • 런타임 시점에 값이 결정되기 때문 
    •  인스턴스 변수의 경우 = Heap(복사본) / 지역변수는 Stack(원본)에 저장 - 객체 생성시 초기화됨 

 

 

JVM의 클래스는 언제 초기화되는가?

 

클래스 초기화 시점

  • new 키워드를 사용한 클래스의 인스턴스 생성
  • 클래스의 static 메소드 호출할 때
  • 클래스의 static 변수 사용할 때 

 

클래스 인스턴스 초기화 순서

 


객체 지향 

키워드 : 부품

객체 지향 프로그래밍 - 부품 

  • 객체(부품)를 만들고 서로 연결해서 더 큰 프로그램을 완성하는 것 

객체 지향 언어 - 부품

  • 소스를 부품으로 보고 조합하는 언어 

JDK 

 : 자바 개발과 컴파일을 위한 도구 

 

* 컴파일 :
인간이 이해할 수 있는 언어로 작성된 소스 코드(고수준 언어 : C, C++, Java 등)를 
CPU가 이해할 수 있는 언어(저수준 언어 : 기계어)로 번역(변환)하는 작업

 

IDE

 : 프로그램 개발 관련된 모든 작업 (코딩, 컴파일, 실행, 배포)을 

하나의 프로그램 안에서 처리하는 환경 제공하는 소프트웨어


Casting (형변환) 

키워드 : 큰 컵, 작은 컵 

묵시적 형변환 : 작은 변수의 자료형을 큰 변수의 자료형으로 (별도의 절차 x)

 

명시적 형변환 : 큰 변수의 자료형을 작은 변수의 자료형으로 (별도의 절차 ㅇ)

 

변수의 자료형

자료형 설명  
byte 1바이트 ↑ 낮음
char 2바이트
int 4바이트 ↓ 높음
long 8바이트
float 4바이트
double 8바이트

메서드

   ★ 

: 어떤 동작을 수행해주는 기능을 함

: 구성) 반환 타입, 메서드 이름, 매개변수, 반환문으로 이루어짐 

: 구성 요소를 일부만 가지고 있을 수 있음 

String 토스트기 (String 빵) {
return "구운빵";


반환 타입 : String
메서드 이름 : 토스트기 

매개변수 : String 빵
반환문 : return

 

 

 


연산자 (Operator)

 

종류 : 단항연산자/ 2항 연산자/ 3항 연산자/복합대입연산자..

 

단항 연산자 : +,- , ++,- - , !.. 등

 

2항 연산자 : +, - , * ,/, %

 

복합 대입 연산자 : +=, -=, *=, /=, %=

 

3항 연산자 : 

      variable = condition? value1 : value2

                           조건         참          거짓


if 조건문과 switch 문

 

if 조건문과 switch 문의 차이점

1. (조건 유무) switch는 조건을 넣을 수 없다. 값만 넣을 수 있다.

2. (값 찾는 방법)  if는 하나 하나씩 확인하여 원하는 값을 찾아냄 / switch는 한번에 원하는 값을 찾아냄(속도 빠름)

 

2. switch의 단점

→ 속도는 빠르나 바로 값을 찾아내기 위해 값을 많이 알고 있어야 해서 메모리를 많이 사용 

 

 

break를 사용해줘야 하는 이유 

프로그램은 코드 블록에서 시작하면 끝까지 달리기 때문에

중간에 빠져나오려면 break를 사용해야 함


반복문

: 자동화를 위해 쓰임

: 특정한 조건이 만족되어야 반복 됨

 

1. for vs while 차이점

for : 종료 횟수정해졌을 경우 

while : 종료 횟수안 정해졌을 경우

 

 

2. do while문 vs while문 차이점

do while

: do 의 내용을 실행 후 while 지속 여부를 체크하는 것

: 즉, 일단 저질러 놓고 조건 체크 (조건이 안 맞아도 한번은 반드시 실행

 

while: 조건이 안 맞으면 수행하지 않음

 


break,continue

break : 빠져나가거나 vs continue : 무시하기


배열

정의)

  • 여러 개의 변수하나의 이름으로 관리하기 위한 자료 구조
  • 타입이 동일한 여러 개의 값을 저장할 수 있는 자료 구조

특징)

  • 선언 시 크기 지정 → 특정 지점을 기준으로 일렬로 배치되어야 하므로 공간이 확보 되어야 해서
  • 첫번째 값 = 0 (특정 기준 변수 [0]로부터 얼마나 떨어졌는가가 이름이 됨)
  • 배열에 저장된 모든 값은 반복문을 이용하여 접근할 수 있음 
  • 모든 데이터가 연속된 공간에 있음 → 빠르게 접근 가능


배열의 생성)

  • new 키워드를 통해 힙 (Heap) 영역의 메모리 할당 받음
    • * 힙 (Heap) : 동적 메모리 할당에 사용되는 프로세스의 메모리 영역 
  • 배열 선언 시 만든 참조변수(배열이름)에 할당받은 메모리의 참조값(주소값을 저장하는 방식)
String[] arr= {"a","b","c"};

int [] score;
score = new int[]{70,80,90}

int[] arr= new int[5];

 

 


2차원, 3차원 배열

 

 


OOP (Object Oriented Programming)                                                                            객체 지향 언어  

 

  • 코드를 부품처럼 모듈화하여 조립하듯이 프로그래밍  (그것을 잘하기 위해 만들어진 언어 = Java)
  • 다른 부품을 잘 사용하기 위해 분류 필요 (package, class)

 

OOP의 장점

재사용성과 유지보수성을 높일 수 있음 

 

 

OOP의 4대 특성 (남의 소스를 편리하게 사용하는 것에 초점)

  • 캡슐화
  • 상속
  • 추상화
  • 다형성 

OOP의 5원칙

: SRP, OCP, LSP, ISP, DIP (SOLID)

SRP
(Single Responsiblity Principle)
단일 책임원칙 
클래스(객체)는 단 하나의 책임만 가져야 한다는 원칙

하나의 클래스는 하나의 기능을 담당
→ 클래스를 따로 따로 여러개 설계 

*  클래스의 단일 책임 
클래스 분리로 이루어짐
OCP
(Open Closed principle)
개방 폐쇄 원칙 
확장에 열려있어야 하며,
수정에는 닫혀있어야 한다
LSP
( Listov Substitution Priciple)
리스코프 치환 원칙
서브 타입은 언제나 기반(부모) 타입으로 교체할 수 있어야 함

업캐스팅된 상태에서 부모 메서드를 사용해도 의도한 대로 흘러가야 함 
ISP
(Interface Segregation principle)
인터페이스 분리 원칙
인터페이스를 각각 사용에 맞게 끔 잘게 분리해야함 

*  인터페이스의 단일 책임
 인터페이스 분리로 이루어짐


인터페이스를 한번 분리하면
또 수정하여 분리하면 안됨 


인터페이스는 다중 구현이 가능함 
DIP
(Dependecy Inversion Principle)
의존성 역전 원칙 
어떤 클래스를 참조할 때는 
그 클래스 상위 요소(추상클래스, 인터페이스) 를 참조하라는 원칙 


구현 클래스가 아닌
인터페이스에 의존하라는 의미 


결합도를 낮추는 것에 초점

 

코드를 확장하고 유지 보수 관리하기가 더 쉬워지며,

불필요한 복잡성을 제거해 리팩토링에 소요되는 시간을 줄임으로써

프로젝트 개발의 생산성을 높일 수 있음

 

 

 

[ 확장에 열려있다 ] - 새로운 변경 사항이 발생했을 때 유연하게 코드를 추가함으로써 큰 힘을 들이지 않고 애플리케이션의 기능을 확장할 수 있음
[ 변경에 닫혀있다 ] - 새로운 변경 사항이 발생했을 때 객체를 직접적으로 수정을 제한함.

 

oop의 장점은 ?

재사용성과 유지보수성을 높일 수 있습니다.

 


Class

각종 메소드와 변수 등을 담는 분류 

클래스의 이름 = 어떤 변수와 함수의 종류 대변하는 이름

 

ex) (클래스 이름) 운동  / (함수) 수영,축구

 

클래스 선언 원칙

1. Java 파일은 최소 1개 이상의 클래스 가짐

2. class는 하나의 자료형이 될 수 있음 (String,Integer,Double)

3. class 안에 class를 선언할 수 있음 

 

클래스 만드는 규칙 

1. 클래스의 첫 글자는 대문자

2. $,_의 특수문자만 가능

3. 클래스와 자바 파일명은 동일해야 함 

 

String에 대문자를 넣는 이유 
자료형이 아니라 클래스이기 때문 

 

 

 

메인 메소드 main()

있는 클래스  실행이 목적
없는 클래스  누군가가 가져다 쓰는 목적 

 

표기법

파스칼 표기법 모든 단어의 첫 문자 =  대문자 
카멜 표기법 의미 있는 첫글자 = 대문자
스내이크 표기법 _로 구분

 

 

클래스 안의 메서드와 변수를 사용하기 위해서는 객체화를 진행해야 함 

 

클래스의 객체화 (instance)

= 원본인 클래스를 복사하여 사용하는 것  

= Static 영역에 저장된 원본 클래스 복사하여 Heap 영역으로 가져오는 것 

= 클래스(원본) / instance(복사본) 

 

복사해서 쓰는 이유

: 원본을 훼손하지 않기 위해서 

담아줄 그릇 / 새로 만듬

 

 

 

 

API (Application Programming Interface)
운영 체제나 프로그래밍 언어가 제공하는 기능을 /  응용 프로그램에서 사용할 수 있도록/ 제어할 수 있게 만든 인터페이스
= 앱을 프로그래밍할 때 사용하는 인터페이스
= 객체화의 설명서가 API

인터페이스 (Interface)
실제로 하면 복잡한 일을 간단하게 할 수 있도록 만들어 놓은 어떤 장치
ex) 메소드 .length() 

 

 

 

자바 8부터 인터페이스에 default 메서드와 static 메서드가 추가되었습니다.

왜 이런 기능이 추가되었으며, 어떤 상황에서 활용될 수 있을까요?

자바 8 이전까지 인터페이스는 오직 추상 메서드(메서드 선언만 가능) 만 가질 수 있었습니다.
default 메서드 : 기존 인터페이스를 변경하면서도 하위 호환성을 유지하기 위해 도입

static 메서드 :  객체 생성 없이 인터페이스 자체에서 기능을 제공하기 위해 추가

 

Java에서 다중 상속은 허용되지 않지만, 인터페이스를 이용하면 다중 구현이 가능합니다. 이때 다이아몬드 문제(Diamond Problem)가 발생할 수 있을까요? 만약 발생한다면 어떻게 해결되나요?

기존의 인터페이스는 다이아몬드 문제가 발생하지 않았습니다. 인터페이스는 구현 코드를 가지고 있지 않고 단순히 규격만을 제공하기 때문입니다.

 

그러나 Java 8 이후 default 메서드가 도입되면서 다이아몬드 문제와 유사한 충돌이 발생할 가능성이 생겼습니다. 

 

다이아몬드 문제란? 

두 개의 부모 클래스로부터 같은 메서드를 상속받을 경우 어떤 메서드를 호출할지 모호해지는 문제

Java는 클래스의 다중 상속을 허용하지 않기 때문에 다이아몬드 문제가 발생하지 않음 

 

해결방법

오버라이딩 또는 super 키워드 사용

 

Java에서 추상 클래스 대신 인터페이스를 사용하는 것이 더 좋은 경우는 언제인가요?

1. 다중 상속(구현) 이 필요한 경우

2. 상태(멤버 변수) 없이 동작(메서드)만 정의할 경우

3. "어떤 행동을 할 수 있다"라는 개념을 표현할 때

4. 여러 클래스에서 공통 동작을 정의할 때

 

인터페이스 기반 개발(Interface-based Programming)이 갖는 장점은 무엇인가요?

1. 코드의 유연성 증가

2. 코드의 재사용성 증가

3. 유지보수성 향상 - 구현체만 수정하면 됨

4. 결합도가 낮춰짐

 

Spring에서 인터페이스 기반 개발이 많이 사용됩니다. 대표적인 예시

* DAO는 데이터베이스(DB)와의 상호작용을 담당하는 객체

비즈니스 로직(Service)과 DB 접근 로직을 분리 → 유지보수성이 향상 

 

 

DAO를 인터페이스로 설계하는 이유

1️⃣ 구현체 교체가 쉬움 → JDBC, JPA, MyBatis 등 다양한 구현체로 변경 가능
2️⃣ 비즈니스 로직과 데이터 액세스 로직의 분리 → 유지보수성이 높아짐

 

 

 

Class의 member

= 같은 클래스에서 묶여 있는 것들 

= 필드, 생성자, 메소드

 

 

필드

클래스 안에 있는 변수 (=멤버 변수) 

 

생성자

클래스를 객체화할 때 초기화하는 수단

초기화 : 최초의 값을 주는 것 ( 0이 아님 )

실제로 무언가를 복사해주는 행위를 하는 것 

 

 

 

메서드 vs 생성자의 차이점

구분 생성자 메서드
반환 타입 없음 있음
이름 클래스 이름과 동일 자유 지정
호출 시점 객체 생성 시 자동 호출 객체 생성 후 원할 때 호출 
역할 객체 초기화 특정 작업 수행 

 

 


Package 

클래스를  종류 별로 담고 있는 상자 (실제로는 폴더)

C:\STUDY\JAVA\Chapter05 → 패키지

 


overload 오버로드 

메소드 명은 같게 / 매개변수 개수와 형태는 다르게 

생성자 오버로드

다양한 객체화를 위해 사용 (다양한 형태의 오버르드를 시켜주고 싶어서) 

더보기

예시

class Person {
    String name;
    int age;

    // 기본 생성자
    Person() {
        this("Unknown", 0);
    }

    // 이름만 받는 생성자
    Person(String name) {
        this(name, 0);
    }

    // 이름과 나이를 받는 생성자
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    void info() {
        System.out.println("이름: " + name + ", 나이: " + age);
    }
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person("Alice");
        Person p3 = new Person("Bob", 25);

        p1.info();  // 이름: Unknown, 나이: 0
        p2.info();  // 이름: Alice, 나이: 0
        p3.info();  // 이름: Bob, 나이: 25
    }
}

 

메소드 오버로드 

사용자가 사용하기 편하게 하기 위해서 

더보기

 

class MathUtils {
    // 두 수의 합
    static int add(int a, int b) {
        return a + b;
    }

    // 세 수의 합
    static int add(int a, int b, int c) {
        return a + b + c;
    }

    // 두 수의 합 (double)
    static double add(double a, double b) {
        return a + b;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(MathUtils.add(5, 3));       // 8
        System.out.println(MathUtils.add(5, 3, 2));    // 10
        System.out.println(MathUtils.add(5.5, 3.2));   // 8.7
    }
}

 


Static  클래스 원본이 자리 잡은 곳

  • Member에 static 키워드 붙이면 클래스 원본 영역에 저장됨 
  • static 멤버는 객체화하지 않고 사용 : 원본 영역이라, 복사본을 만드는 객체화는 하지 않음
  • static은 공통적으로 원본을 건들여야 하는 부분에 붙이기 

static과 static이 아닐 때 차이점

 

A. 다른 클래스에 있는 멤버를 가져올 때 

//static 멤버는 원본으로 직접 가서 가져옴 (객체화하지 않음)
System.out.println(Sub.sFieled);
        
        
//일반 멤버들은 객체화해서 사용하면 된다.
Sub sub = new Sub();
System.out.println("field :" +sub.field);

 

B. 같은 클래스에 있는 static과 일반 가져오는 법 

 

(static) st_msg("hello static member");

	//static 영역
	static void st_msg(String msg) {
		System.out.println("static 멤버 메서드: " + msg);
	}

 

(일반) 객체화해서 가져옴 

Inner inner = new Inner();

inner.h_msg("Hello,member");

//일반 영역
	void h_msg(String msg) {
		// st_msg 가져오기
		// ★★★ 얘가 static인 st_msg가져오려면 원본 영역 가서 불러와야함
		Inner.st_msg("static message from heap"); // 원본 class명.불러오고 싶은 것 //inner. 하지 말기
		System.out.println("일반 멤버 메서드 :" + msg);
	}

 

(한 클래스 안의 일반 영역에서 static을 가져오는 법)

원본 영역에서 가져옴

	void h_msg(String msg) {
		// st_msg 가져오기
		// ★★★ 얘가 static인 st_msg가져오려면 원본 영역 가서 불러와야함
		Inner.st_msg("static message from heap"); // 원본 class명.불러오고 싶은 것 //inner. 하지 말기
		System.out.println("일반 멤버 메서드 :" + msg);
	}

final (= readOnly)

  • 생성자에 의해서만 초기화 가능
  • 한번 지정되면 프로그램 종료 시까지 변경 불가

static final (상수)

상수는 초기화 값을 주고 시작함 

final은 생성자를 사용할 수 있지만, static은 원본이기 때문에 복사본을 만드는 생성자를 쓸 수 없기 때문  


Utility

상수만 모아둔 클래스 (대문자) 

 


import

같은 패키지 내의 클래스에서는 쓸 필요가 없으나,

다른 패키지에 있는 클래스는 import해줘야 함

public 붙인 클래스들

 


접근 제한자 

사용할 수 있는 영역을 지정

접근 제한 적용 대상 접근 허용
public 클래스, 필드, 생성자, 메서드 모두
protected 필드, 생성자, 메서드 extends
패키지 다르면 부모 자식 관계만
접근 가능
default 클래스, 필드, 생성자, 메서드 같은 패키지
private 필드, 생성자, 메서드 같은 클래스

 

 

 


캡슐화

특정 부분의 접근을 제한하는 것

사용자가 잘못 건드릴까봐 미리 방지하는 것 (소스 안보이게 하는 것 x)

 

예시) PC 본체 


getter,setter

private은 원래 같은 클래스에서만 접근을 허용함 

private 클래스를 외부 클래스에서 사용하려고 할 때 쓰는 것

 

get = 보여주고 싶을 때

set = 조정하게 하고 싶을 때 


OOP 1. 상속 inheritance

= extends : 기능/분류의 확장 

= 일반화 (Generalization)

나에게 없는 것을 부모에게 물려 받아 원래 내 것처럼 사용하는 것

ex) 포유류 - 인간

 

 

부모 클래스의 private 상속 불가능

 

 

 

하는 이유

1) 코드의 중복을 피할 수 있음

2) 내 능력으로는 만들지 못하는 기술을 쓸 수 있음 

 

 

 

상속의 특징

1. 상속 가능하려면 부모 있어야 함   

2. 상속 받으면 해당 메서드를 부모 클래스 호출 없이 사용 가능 

3 .자바는 다중 상속 안됨 : 클래스는 분류라서 하나에만 속할 수 있기 때문

4. 자식 생성자가 실행될 경우 자동으로 부모 생성자 먼저 호출  (super() 생략)

 

child 호출 → parent 먼저 생성 → child 생성

 

Mamal (부모 클래스) 

package chap06.ex01;

public class Mamal {
	public void birth () {
		System.out.println("새끼를 낳다.");
	}
	
	public void eat() {
		System.out.println("젖을 먹는다.");
	}

}

 

Person

package chap06.ex01;

public class Person extends Mamal{
	//birds와 mamal중 하나만 상속 가능
		//mamal만 상속하자
	public void useTool() {
		System.out.println("도구를 활용 한다.");
	}
	
	public void social() {
		System.out.println("사회 생활을 한다.");
	}
	
	public void talk() {
		System.out.println("대화를 한다.");
	}

}

 

Main

자식을 객체화해도 부모의 기능을 사용할 수 있음 

package chap06.ex01;

public class Main {

	public static void main(String[] args) {
		//객체화 하기
		//Person을 객체화 했지만
		//Mamal의 기능도 사용 가능하다.
		Person person = new Person();
		person.birth();//Mamal 것
		person.eat(); // Mamal 것
		
		person.useTool();//Person 것
		person.social(); // Person 것
		person.talk(); // Person 것
	}

}

결과 : 
새끼를 낳다.
젖을 먹는다.
도구를 활용 한다.
사회 생활을 한다.
대화를 한다.

상속 vs 객체화

객체화) 사용하고 싶은 메서드에 따라 각각 객체화를 해야 함 

상속) 각각을 상속함으로써 Operator 하나만 객체화해도 많은 메서드를 사용할 수 있음

 

 

final 붙은 메서드는 읽기 전용이라 오버라이드 불가 

 

다형성

: 같은 부모를 상속받은 클래스는 같은 부모 타입으로 들어갈 수 있음

: 자식들이 부모의 형태로 들어갈 수 있는 것  

ex) 포유류 폴더 (부모) / 인간, 고래, 개 사진(자식)

 

 

묵시적 형변환 : 자식 -> 부모 형변환

명시적 형변환 : 부모 -> 자식 형변환

 

부모 형변환한 자식은 자신의 기능을 다시 사용하려면 다시 본인의 클래스로 캐스팅해야함 

 

public static void main(String[] args) {
		Mammal mal = null;

		// 부모 형태로 묵시적 형변환(다형성)하면
		mal = new Dog();
		mal.birth();
		mal.eat(); // 부모 것은 가져올 수 있음
		// mal.bark(); // 개의 고유 특성(자기 메서드)는 사용할 수 없음

		// 나의 고유 특성을 살리고 싶다면?
		// 명시적 형변환으로 나의 특성(집)으로 되돌아 가야 한다.
		// 포유류에서 돌아갈 때 반드시!!! 이전 형태인 개로 다시 돌아가야함 - 다른 동물로 가면 안됨
		Dog dog = (Dog) mal; // 오류 : Dog dog = mal; => Mamal에서 dog로 가려하니까 안됨
		dog.bark();

 

 

 

 

필드 다형성

변수 = 필드 

 

 


7

추상화 

: 공통되는 특징을 추출하여 단순화 시키는 작업

쓰는 이유) 결합도를 느슨하게 함 

 

추상 클래스

: 규격 만들 때 사용  

: 실체 클래스는 추상 클래스를 상속 (상속 관계) 

 

일반 클래스와 추상화 클래스의 차이점

1. 추상화는 자식만 객체화할 수 있음 

(추상 클래스 생성자에 접근하고 싶으면 자식 클래스에서 super() 호출)

추상화는 부모를 객체화 시킬 수 없다 

 

2. 추상 클래스만 추상 메서드 가짐

 

 

추상 메서드 ( abstract 키워드 붙임)

: 형태만 있고, 몸체는 없는 메서드  = 이름만 있고, 행위 자체가 없음(코드 블럭이 없음)

: 특정한 메서드를 강제로 override해야할 경우 설정 

: 자식이 꼭 오버라이드해야 사용할 수 있는 메서드 

 

: 상속과 동시에 강제 override됨 (= 규격은 꼭 해야하는 것을 정해둔 것 - 필요한 것을 강제로 오버라이드)  

 

예시) 

추상메서드

package chap07.ex02;

public abstract class Abs {
	public void parent() {
		System.out.println("그냥 쓰거나 오버라이드 해도 되는 메서드");
	}
	
	//추상 메서드(몸체가 없는 메서드 : 만들어도 실행이 안된다는 뜻 - 실행할 것이 없는데) 
	//몸체 만들거나 추상화 해!
	//추상은 상속관계에서만 사용할 수 있고
		//자식이 오버라이드하는 용도로 사용
	//★★★  자식이 꼭 오버라이드해야 사용할 수 있는 메소드
	public abstract void must1();
	public abstract void must2();
	
}

 

자식 메서드 

package chap07.ex02;

public class Concrete extends Abs {
	
	@Override
	public void must1() {
		// TODO Auto-generated method stub

	}

	@Override
	public void must2() {
		// TODO Auto-generated method stub

	}

}

 

 

캡슐화 : 다른 사용자에게 데이터 입/출력을 제한

상속 : 나에게 없는 기능을 부모에게 받아서 내 것처럼 씀

다형성 : 부모의 형태에 자식이 들어갈 수 있다. 

 

추상화는 공통되는 특징을 추출하여 단순화 시키는 작업
추상 클래스는 abstract 키워드를 사용
추상 클래스는 자식 이외에는 객체화 시킬 수 없음
추상 클래스를 상속 받으면 추상 메서드를 강제로 override
이 과정을 통해 꼭 필요한 메서드를 만들어 규격화시킬 수 있다.

 

 

예상 질문) 

1. 개념 관련

추상화(Abstraction)란 무엇인가요?
추상화를 사용하는 이유는 무엇인가요?
-결합도를 느슨하게 만들기 위해
(코드의 유지보수성과 확장성을 높임)

- 필수적으로 구현해야 하는 기능을 강제하기 위해


추상 클래스(Abstract Class)란 무엇인가요?
객체를 직접 생성할 수 없는 클래스
공통된 기능을 정의하고, 일부 메서드는 자식 클래스에서 강제 구현하도록 함


일반 클래스와 추상 클래스의 차이점은 무엇인가요?
일반 클래스는 객체 생성을 직접할 수 있고, 추상 메서드를 사용할 수 없음 - 목적은 기능 구현
추상 클래스는 객체를 직접 생성할 수 없고 상속 후 자식이 객체화, 추상 메서드를 사용할 수 있음 -
목적은 공통 규격 제공
 


추상 클래스와 인터페이스의 차이점은 무엇인가요?


추상 클래스는 객체를 직접 생성할 수 있나요?
아니요, 직접 생성할 수 없습니다.
자식 클래스를 통해 객체를 생성할 수 있음

추상 클래스에서 생성자를 사용할 수 있나요?
네, 생성자를 가질 수 있습니다.
자식 클래스에서 super()를 호출하여 부모의 생성자를 실행

추상 클래스에서 일반 메서드를 정의할 수 있나요?
가능 - 추상 메서드와 일반 메서드 포함할 수 있음

추상 클래스가 포함할 수 있는 요소에는 어떤 것들이 있나요?

멤버 변수 (인스턴스 변수, 정적 변수), 생성자, 일반 메서드, 추상 메서드

추상 메서드(Abstract Method)란 무엇인가요?
메서드의 선언만 있고 구현이 없는 메서드
자식이 반드시 오버라이드해서 써야 함 

추상 클래스에서 추상 메서드를 선언하는 이유는 무엇인가요?
필수적으로 구현해야 하는 기능을 자식 클래스에서 강제하기 위해

추상 메서드는 반드시 오버라이드해야 하나요? 추상 클래스가 자식 클래스에게 강제하는 것은 무엇인가요?
바 

 

 

인터페이스

- 객체 사용방법 정의 

- 실제로 하면 복잡한 걸 쉽게 할 수 있게 해주는 것 

- 목적 = 규격화 

- 역할 = 추상화 메서드를 통해 공동 작업 시 규격을 잡아주는 역할

 

인터페이스 vs 추상화의 차이점

인터페이스는 클래스 뿐만 아니라 '규격'만을 위한 것

추상클래스 : 공통적인 기능을 정의 (부모 역할), 클래스는 분류의 개념이라 하나만 상속 가능

인터페이스 : 규격(행동)을 정의 (강제적인 역할 부여) , 복수의 인터페이스 구현 가능

 

 

 

항목 Abstract interface
키워드 abstract interface
추상 메서드 abstract 키워드 추가 시 사용 가능 키워드 추가 없이 사용 가능
일반 메서드 사용 가능 default 키워드 추가 시 사용 가능 (jdk 1.8)
정적(static)메서드 사용 가능 사용 가능(jdk 1.8)
Static은 원본 영역으로 가져오기 
사용 extends 하여 사용 implements 해서 사용
사용 개수 1개의 abstract class 상속 가능 복수 개의 interface 구현 가능

: 클래스는 분류의 개념이라 하나만 상속 가능하고 ,
인터페이스는 규격의 개념
- 여러가지 규격이 있을 수 있고, 하나의 규격만 있다는 것은 말도 안됨
객체화 자식 객체만 가능 불가능

 

Interface 는 클래스가 반드시 따라야 하는 규격(행동)을 정의하는 추상적인 타입

Interface 는 객체화가 불가능 하다.
Interface 는 기본적으로는 추상화 메서드를 사용한다.
Interface 를 통해서도 특정 기능 사용에 대한 규격을 설정 할 수 있다.



추상 클래스와 인터페이스를 언제 사용해야 하나요?

추상 클래스 사용:
공통된 구현을 여러 클래스에서 공유해야 하며, 각 클래스가 특정 부분을 수정해야 할 때. 
자식 클래스가 반드시 일부 메서드를 오버라이드해야 할 때.

인터페이스 사용:
클래스가 여러 다른 타입을 구현할 수 있어야 할 때 (다중 상속을 통한 확장 가능성).
기능을 제공하는 계층을 만들 때.
클래스 간의 관계가 아니라, 특정 행동을 하도록 강제하고 싶을 때 (예: Serializable, Comparable).

 

인터페이스는 왜 다중 구현이 가능할까?

인터페이스는 기본적으로 "구현 코드"가 없고 ' 규격만 정의 '하기 때문

구현은 자식 클래스가 맡기 때문에, 여러 개의 인터페이스를 동시에 구현할 수 있습니다.

 

모듈식 구조라서 필요한거 언제든 가져와서 쓸 수 있음

 

 

default 메서드는 인터페이스에서 구현부를 제공하여 다중 구현 시 책임을 명확히 하며,

충돌이 발생하면 자식 클래스에서 이를 해결하도록 강제해서 다중 구현 가능.

 



 

 

 

인터페이스 다형성

다형성은 내 것을 잃어버림 / 부모 것밖에 못 사용함 

 

인터페이스 익명 객체

인터페이스는 객체화되지 않기 때문에 익명 객체 활용 (여기에서 한번만 쓰겠다는 의미) 

 

A,B를 구현 받았을 때 둘의 기능을 한번에 쓰는 방법

A,B중 하나가 서로 상속 받은 후 그걸 활용하는 것

 

클래스 VS 인터페이스 상속

인터페이스 :  다중 상속 가능 / 모듈 구조 (implements)

클래스 : 다중 상속 x / 위계 구조 (extends) // 클래스는 순차적으로 기능을 extends 해나가는 위계구조

 

인터페이스는 인터페이스끼리만 상속 (인터페이스와 클래스끼리는 상속 안되고 구현만 됨)

 

 

  • 클래스와 다르게 인터페이스는 모듈 구조를 가지고
  • 그렇기 때문에 복수 개의 인터페이스를 구현 및 상속이 가능함

Q. 추상화를 사용하는 이유 (규격 제공 ⇒ 결합도 낮아짐)

  • 추상화를 사용하면 규격이 필요하다
  • 규격을 제공하면 서로 다른 것들끼리 충돌 없이 사용할 수 있어서 결합도 낮아짐

 

 

Interface 를 사용 하면 결합 도를 낮출 수 있다.

규격이 있다면 서로 다른 것들끼리 충돌 없이 사용 할 수 있기 때문 이다.

 

 

 

 

 


StringBuffer와 StringBuilder

- 10개 이상의 문장을 붙여야 하면 StringBuffer와 StringBuilder

- 용량이 변화하므로 새로운 객체를 생성하지 않음

 

 

StringBuffer와 StringBuilder 차이

  • StringBuffer은 다수 유저 동시 접근 허용 x
  • StringBuilder은 다수 유저 동시 접근을 허용 o

 

StringBuffer

- append (추가) / reverse (문자열 뒤집기) 

→ 끝나면 .toString()  : buffer은 객체라 문자열로 변환하여 출력 

 

 


String은 char배열을 다루기 위한 클래스

String에는 문자열을 다룰 수 있는 여러 메소드들이 존재

String에서 문자열을 추가할 때마다 객체가 증가

 

 

StringBuffer는 스레드 안전(synchronized)하게 설계되어 여러 쓰레드에서 동시에 사용

반면, StringBuilder는 스레드 안전하지 않지만 성능이 더 우수

 

 


9

예외,에러

에러 (개발자 문제) : 문법이나 메모리 사용 문제로 실행에 문제가 생겨 발생하는 오류

예외 (사용자 문제) : 사용자의 오 조작, 외부 문제 

 

예외 종류

1. Exception : 컴파일 타임에 실행되는 예외 / 코딩 중에 알 수 있는 빨간 줄 

2. Runtime Exception : 런타임에 실행되는 예외 / 실행해봐야 알 수 있음 

 

일반 예외는 JVM에서 필요하다고 판단되면 자동으로 예외처리

 

Runtime exception의 종류

NullPointException 객체가 없는 상태에서 객체 이용할 경우
ArrayIndexOutOfBoundException 배열 인덱스 범위를 초과하여 사용할 경우
NumberFormatException 숫자 변환 시 문자가 포함되어 있는 경우
ClassCastException 매개 값으로 받은 타입의 종류를 알 수 없을 경우 

예외 처리

1. try - catch : 내가 해결하겠다 

: try 영역 안에서 exception 발생 시 catch를 실행하는 구문 

: finally는 exception 발생 시에도 꼭 실행할 코드 작성

: 예외가 많으면 multi catch 혹은 Exception 하나로 사용하기 

 

e.printStackTrace();는 개발 후엔 주석 처리 

 

2. throws : 나에게 일을 시킨 메소드에게 전가 

: main()에서도 throws하면 JVM에서 처리하게 됨 

: 하는 이유) 예외 처리의 중복 기능 막기 위함 


9-2

컬렉션 프레임워크

: 컬렉션 인터페이스를 최상위로 하는 자료구조 인터페이스

: 초기 선언 시 크기를 지정하지 않아 유연한 사용이 가능 

: 크기 제한이 없음 (거의 무한함) 

: 리스트, 셋 인터페이스는 비슷함 - 같은 컬렉션 인터페이스를 상속받아서 

 

리스트,셋,맵

 

 

인터페이스 구현 시 장점 : 필수 메서드를 강제할 수 있음 

 

List

1) ArrayList 

: 배열처럼 인덱스로 객체를 관리함

: 인덱스가 유연함 - 객체 삭제/ 추가 시 인덱스가 1씩 당겨지거나, 미뤄짐

: 그래서 빈번하면 무리감 - 삭제될 때마다 양옆이 붙음 

추가 .add()
수정 .set (인덱스, 덮어쓸 값)
(특정 인덱스의 값을 덮어쓴다.)
검색  .contains();
특정값의 인덱스 검색 .indexOf(); 
모든 데이터 삭제 .clear();
비워진 상태 맞는지 .isEmpty();

 

 

2)  LinkedList

: 좌우 데이터 주소를 기억하는 구조

 

 

3) Vector

: 특정 쓰레드 접근 시 다른 쓰레드가 접근할 수 없음 (누군가가 사용하고 있으면 다른 사람은 기다려야 함) 

 

 

배열을 List로 바꾸는 방법

List<String>  list = Arrays.asList(arr);

 

List를 ArrayList로 바꾸는 방법

ArrayList<String> arrList = new ArrayList<String>();

arrList.addAll(list);

 

 

//뒤에 쭉 값 들어가면 Arraylist

//넣다 빠졌따 빈번 = linkedlist 사용하기 

 


Set 컬렉션 

순서 없음 / 중복 안됨

검색 기능 없음 - 대신 Iterator 있음

(검색의 기본은 정렬, 근데 set은 순서가 없어서 정렬 x , 검색도 x -> 일일히 Iterator로 꺼내봐야 함) 

 

 

Set의 Iterator

Set<String> set = new java.util.HashSet<String>();

Iterator<String> iter = set.iterator();

while (iter.hasNext()) {

String item = iter.next();

 

Map

key와 value로 이루어진 자료 구조

key는 순서 없고, 중복 없음 

 

모든 값을 가져오는 방법

1. key를 set으로 가져와서 key를 iterator

 

2. Map을 Set으로 가져와 key/value 단위로 iterator

 

Hash맵 vs Hash테이블

Hash맵 : 다른 사람 들어와도 됨

Hash테이블 : 들어오면 안됨

 

 

 


 스택과 큐

스택 : LIFO (후입선출) (push, pop) 

큐 : FIFO(선입선출) - 순서대로 작업할 때 유리  (offer,poll)

 

큐는 내 뒤에 무슨 일이 있는지, 앞에 무슨일이 있는지 알아야하기 때문에

큐는 인터페이스만 있음, 형태만 있고, 실질적인 구조는 링크드 리스트


11

Stream : 시스템 안 또는 밖으로 이동하는 Data의 흐름 

 

바이트 기반: InputStream / OutputStream

문자 기반 : Reader, Writer

 

바이트 기반

문제점: 문자를 제대로 취급못함

 

문자 기반

순수한 txt의 텍스트 파일

 

 


13

쓰레드 : 프로그램을 실행하게 해주는 원동력 

ex) main(String[]args)가 쓰레드를 동작하게 해주는 메소드 

 

 

프로세스 VS 쓰레드

1. 쓰레드는 프로세스에 소속  : 하나의 프로세스에는 한개 이상의 쓰레드가 존재함 

2. 메모리 공유

프로세스 간에는 메모리 쉐어 X

쓰레드 간에는 메모리 쉐어 O

 

 


메인 메소드가 없으면 프로그램 실행 안됨 : 메인 메소드가 메인 쓰레드를 만들기 때문 

 

메인쓰레드는 자신을 위해 일할 워크 쓰레드들 생성 = 멀티 쓰레드

 

멀티 쓰레드 단점

: 나의 복제본이 내 말을 안 들음 

 

 

쓰레드를 만드는 2가지 방법

1) Runnable 인터페이스 구현

2) Thread class 상속 방법 

(익명 객체 활용) 

 

 

쓰레드의 진짜 문제점 (왜 말을 안 들을까?)

쓰레드는 순서 제어가 어려움

그냥 빠른 사람이 더 빨리 오는 것

 

 

Round Robin (RR)

돌면서 처리하는 것

먼저 시작했다고 먼저 끝나지 않음

빨리 처리하면 다음 일을 받는 방식

먼저 출발하는 사람이 먼저 도착하는 법 없다, 무조건 속도 우선!!!!!

 

쓰레드 문제점 총정리

1. 순서 문제 : 내 마음대로 제어 안됨

2. 데이터가 엉망진창될 수 있음 (쓰레드끼리는 메모리 공유 > 객체 간의 데이터 간섭이 일어나는 문제)

 

 

방지 방법 : 동기화 

다 사용할 때까지 대기하게 하는 것

 

동기화 사용하는 방법

1) 동기화 메소드 : 다 사용할 때까지 들어오지 않게 (식당 밖)

2) 동기화 블록 : 들어와도는 되지만 특정 영역에 들어오지 못하게 하는 것 (식당 안) 

 

 

쓰레드 제어 방법 3가지

1. sleep : 스스로 정해진 시간만큼 쉬는 것 

2. yield 양보 : 상대에게 실행할 기회를 주는 것

- 양보했을 때 괜찮다고 하면 그냥 알겠어~하고 앉아버리는 느낌 

3. join - blocking : 특정 쓰레드의 종료를 기다리는 것 

: 쓰레드 A끝나도 쓰레드 B가 내려와서 둘이 서로 만날때까지 기다리라는 것 

 

13

동기화 - 메서드 다 할 때까지 들어오지 않게 하는 것 

동기화 블럭 방식 = 들어와도 되는데, 특정 영역만 들어오지마 → 식당 안에서 기다리게 함