1. 인스턴스 변수, 전역 변수(클래스 변수), 지역 변수의 정의와 차이
# 클래스 변수와 인스턴스 변수는 멤버변수라고도 함
- 인스턴스 변수
- 객체지향 프로그래밍에서 사용
- 클래스 내부의 멤버 변수로 선언하여 사용
- 클래스의 생성자를 통해 초기화 되고 해당 클래스의 모든 인스턴스에서 사용가능
- 객체 생성 없이는 사용 불가능
- 각 인스턴스 마다 서로 다른 값을 가질 수 있음
- 인스턴스마다 독립적으로 존재하며 객체의 상태를 나타내는 데 사용
public class MyClass {
int instanceVariable; // 인스턴스 변수 선언
public MyClass(int value) {
instanceVariable = value; // 생성자를 통해 인스턴스 변수 초기화
}
}
- 전역 변수(클래스 변수)
- java에서는 엄밀히 따지자면 전역변수가 없음. static을 사용하여 정의한 멤버변수를 해당 클래스의 모든 인스턴스에 공유되는 변수로 간주할 수 있음
- 프로그램 전체에서 접근 가능
- 객체 생성 없이 사용가능 하고, 프로그램이 시작될 때 생성되어 종료될 때까지 유지됨
- 전역변수를 많이 사용할 경우 프로그램이 복잡해지고 디버깅이 어려움
- 프로그램 전체에서 접근 가능하며 주로 공유 데이터를 저장하기 위해 사용
public class MyClass {
static int globalVariable; // 클래스 변수 선언
public static void main(String[] args) {
globalVariable = 10; // 클래스 변수에 값 할당
}
}
- 지역 변수
- 변수가 선어된 블록이나 함수 내에서만 접근 가능
- 해당 블록이나 함수의 실행이 완료되면 소멸됨
- 변수가 선언된 블록이나 함수 내에서만 접근 가능하며 임시 데이터를 저장하는 데 사용
public class MyClass {
public void myMethod() {
int localVar = 5; // 로컬 변수 선언
if (localVar == 5) {
int innerVar = 10; // 중첩된 블록 내의 로컬 변수 선언
}
// localVar는 여기서는 접근 가능하지만 innerVar는 접근 불가능
}
}
1-1. 각각의 변수가 저장되는 위치
- 인스턴스 변수
- 객체가 생성될 때 Heap 메모리 영역에 저장
- 각 객체마다 독립적으로 메모리를 할당받고, 객체의 상태를 나타내는 데이터 저장됨
- 객체의 생명주기 동안 유지되며, 객체가 더 이상 참조되지 않을 때 소멸됨
- 전역 변수(클래스 변수)
- 클래스의 static 멤버 변수로서 클래스의 정적 데이터 영역(static data area)에 저장
- 프로그램이 실행되면서 클래스의 정적 변수들이 초기화되고 프로그램 종료 시까지 유지됨
- 지역 변수
- 해당 블록이나 메서드가 실행될 때 메모리의 스택(Stack) 영역에 저장
- 스택은 메서드 호출 시 생성되는 지역 변수와 임시 데이터를 저장하는 데 사용되며, 메서드의 실행이 종료되면 해당 스택 프레임과 함께 로컬 변수도 소멸됨
2. Inner Class(내부 클래스)의 장점
# 하나의 클래스 내부에 선언된 또 다른 클래스
# 외부 클래스(outer class)와 긴밀한 관계를 맺고 있을 때 선언할 수 있음
- 캡슐화와 은닉성 강화
- 내부 클래스는 외부 클래스의 멤버에 쉽게 접근할 수 있음
- 이로 인해 관련된 클래스와 데이터를 더 쉽게 캡슐화하고, 내부 클래스의 세부 구현을 외부로부터 숨길 수 있음
- 모듈화
- 내부 클래스는 외부 클래스와 논리적으로 밀접한 관계를 가질 때 유용
- 외부 클래스와 내부 클래스 간에는 더 높은 결합도와 더 낮은 응집도를 유지할 수 있음
- 이는 클래스 간의 관련성을 강화하고, 코드를 모듈화하여 관리하기 쉽게 만들어 줌
- 코드의 가독성 향상
- 내부 클래스를 사용하면 관련된 코드를 논리적으로 그룹화할 수 있어 코드의 가독성을 향상시킬 수 있음
- 관련 있는 클래스를 그룹화하여 코드의 복잡성을 줄일 수 있음
3. 배열과 리스트의 차이
배열 | 리스트 | |
크기 | - 생성할 때 크기 지정. 크기를 변경할 수 없음 - 배열의 크기를 변경하려면 새로운 배열을 생성하고 기존 데이터를 복사해야함 |
- 동적으로 크기 조절 가능 - 제한없이 원소 추가, 제거 가능 |
데이터 타입 | - 동일한 데이터 타입의 원소만 저장할 수 있음 | - 다양한 데이터 타입의 원소를 저장할 수 있음 (but 타입 안정성이 감소하고 런타임 에러의 가능성이 높아 사용지양) - 자바의 제네릭을 사용하여 타입 안정성 유지 |
메모리 할당 | - 일반적으로 연속된 메모리 공간에 원소들을 저장 | - 각 원소는 다음 원소의 위치를 가리키는 방식으로 저장 |
접근 방식 | - 인덱스를 통해 원소에 접근할 수 있음 - 접근 속도가 빠름 |
- 처음부터 순차적으로 접근해야함 - 배열보다 접근 속도가 느릴 수 있음 |
기능 및 연산 | - 주어진 크기의 고정된 원소를 관리하는 간단한 구조 - 특별한 메서드나 연산을 제공하지 않음 |
- 원소의 추가, 삭제, 검색 등 다양한 연산을 위한 메서드 제공 (예: ArrayList, LinkedList 등) |
구현과 사용 |
- 언어의 기본 자료구조로서 지원 - 선언 후 바로 사용 가능 |
- 언어에 따라 다양한 형태의 리스트가 구현될 수 있음 - 표준 라이브러리나 외부 라이브러리를 통해 사용 가능 |
# 데이터 크기가 고정되어 있고 빠른 접근이 필요한 경우에는 배열을 사용하고,
데이터 크기가 동적으로 변하며 다양한 연산이 필요한 경우에는 리스트를 사용
3-1. 동적 할당이 필요한 경우
# 정적할당 시 stac 영역에 저장되고, 동적할당 시 heap 영역에 저장됨
- stac 영역
- 컴파일 되며 크기가 결정됨
- 지역변수, 매개변수, 반환값 등이 저장됨
- 함수가 호출된 때 생겨나고 종료 시 사라짐
- 다른 함수나 스레드의 값들을 참조할 수 없음
- 선입후출 방식
- heap 영역
- 런타임에 크기가 결정됨
- heap 영역에 저장하는 경우
- 데이터 배열의 크기가 일정하지 않거나 변동이 있을 때
- 프로그램 전반에서 해당 주소를 참조해야하는 경우
- 모든 스레드에서 값을 공유
- 사용이 끝난 메모리는 해제하지 않으면 메모리 누수가 발생하여 cpu를 지연시킬 수 있음
- 선입선출 방식
- 데이터 크기가 변경될 가능성이 있을 경우
- 프로그램 실행 중에 데이터 크기가 변경되는 경우 동적 할당이 필요함
- 예를 들어, 사용자가 입력하는 항목의 개수가 변할 수 있는 경우 배열보다는 ArrayList나 LinkedList와 같은 동적인 데이터 구조를 사용하여 메모리를 효율적으로 활용할 수 있음
- 동적으로 생성되는 객체
- 프로그램이 실행 중에 동적으로 객체를 생성해야 할 때 동적 할당이 필요함
- 예를 들면, 사용자가 요청한 작업에 따라 다른 유형의 객체를 생성해야 할 경우
- 메모리 효율성
- 한 번에 모든 데이터의 메모리를 할당하는 것은 비효율적이고 프로그램의 구조나 요구 사항 변경에 대응하기 어려울 수 있음
- 동적 할당을 사용하면 필요한 메모리만 사용하여 메모리 효율성을 높이고 코드 수정 없이도 프로그램을 확장하거나 변경할 수 있음
- 예를 들어, 파일의 내용을 읽어와서 처리해야 할 때 모든 내용을 한 번에 메모리에 로드하는 대신 필요한 만큼만 메모리를 할당하면서 작업할 수 있음
- 재귀적인 데이터 구조
- 재귀적인 데이터 구조에서는 데이터의 깊이나 수준을 예측하기 어려워 트리나 그래프와 같은 구조에서 노드를 동적으로 추가하거나 제거해야 할 때 동적 할당이 필요함
# ArrayList, LinkedList, HashMap과 같은 자료구조를 활용하여 동적 할당을 수행할 수 있음
4. 기본형 형변환, 참조형 형변환의 정의와 차이
- 기본형 형변환 (Primitive Type Casting)
- 하나의 기본 데이터 타입을 다른 기본 데이터 타입으로 변환하는 것
- 기본형 형변환은 값 자체의 변환으로 이루어지며, 주로 작은 크기의 데이터 타입에서 큰 크기의 데이터 타입으로 변환하거나, 데이터의 손실을 감안하고 큰 크기의 데이터 타입에서 작은 크기의 데이터 타입으로 변환할 때 사용
- 예를 들어, int를 double로 변환하거나 double을 int로 변환하는 것
- 큰 범위에서 작은 범위로 형변환 시 데이터 손실이 발생할 수 있음
- 참조형 형변환 (Reference Type Casting)
- 클래스나 인터페이스와 같은 참조형 데이터 타입 간의 변환
- 참조형 형변환은 상속 계층이나 인터페이스 구조에 따라 이루어짐
- 이러한 형변환은 실제 객체의 형태나 내용을 변경하는 것이 아니라, 참조 변수의 타입을 변환하는 것임. 참조형 형변환은 다형성을 활용하여 다른 클래스들 간에 상호 작용을 편리하게 할 수 있도록 도와줌
- 업캐스팅(Upcasting)
- 두 클래스가 부모-자식 관계에 있을 때, 자식 클래스의 인스턴스를 부모 클래스의 참조 변수로 가리킬 수 있음
- 이 경우 명시적으로 타입 캐스팅을 하지 않아도 자동으로 처리
- 다운캐스팅(Downcasting)
- 부모 클래스의 참조 변수를 자식 클래스의 인스턴스로 가리키는 것
- 이 경우 명시적인 타입 캐스팅이 필요
4-1. 기본형과 참조형 변수의 차이
기본형 변수 | 참조형 변수 | |
데이터 저장 방식 | 실제 값을 변수에 저장 | 실제 값의 메모리 위치(참조)를 변수에 저장 |
메모리 사용 | 메모리 사용이 적음 | 메모리 사용이 많으나 GC를 통해 메모리 관리 가능 |
종류 | int, double, char, boolean 등 | - 클래스, 인터페이스, 배열 등과 같이 객체나 데이터의 구조를 나타내는 데이터 유형 - String, 사용자 정의 클래스, 배열 등 |
Null | 불가능 | 가능 |
메서드 호출 시 | - 해당 변수에 저장된 값이 메서드로 전달 - 메서드 내에서 변수 값이 변경되어도 호출자에게 영향을 미치지 않음 |
- 해당 변수가 가리키는 객체의 참조(메모리 위치)가 메서드로 전달 - 메서드 내에서 객체의 내용이 변경될 수 있고, 이는 호출자에게 영향을 미칠 수 있음 |
5. 추상화, 다형성, 상속의 개념 및 예시 코드
- 추상화 (Abstraction)
- 복잡한 현실 세계를 단순화하여 중요한 특징을 강조하는 것( ex) 약도)
- 객체의 공통적인 속성과 기능을 추출하여 정의하는것
- 어떤 객체가 수행해야 하는 핵심적인 역할만을 규정해두고, 실제적인 구현은 해당 인터페이스를 구현하는 각각의 객체들에서 하도록 함
- 추상화를 통해 프로그램의 복잡성을 감소시키고 필요한 정보만을 남겨 더 효율적으로 다룰 수 있음
- 다형성 (Polymorphism)
- 같은 이름의 메서드나 연산자가 다양한 형태로 작동할 수 있는 능력
- 메서드 오버로딩과 메서드 오버라이딩을 통해 구현됨
- 다형성을 통해 코드 재사용성이 높아지며, 객체의 다양한 유형을 하나의 일반적인 인터페이스로 다룰 수 있음
- 상위 클래스 타입의 참조변수로 하위 클래스의 객체를 참조할 수 있도록 하는 것
- 상속 (Inheritance)
- 하위 클래스(subclass)가 상위 클래스(superclass)의 특성과 동작을 상속받아 사용하는 것
- 상속을 통해 클래스 간에 계층 구조를 형성하여 공통된 코드를 재사용할 수 있으며, 다형성을 구현하는 기반이 됨
// 추상화 예시: 추상 동물 클래스
abstract class Animal {
String name;
Animal(String name) {
this.name = name;
}
abstract void makeSound(); // 추상 메서드, 하위 클래스에서 구현 필요
}
class Dog extends Animal {
Dog(String name) {
super(name);
}
@Override
void makeSound() {
System.out.println(name + " barks");
}
}
class Cat extends Animal {
Cat(String name) {
super(name);
}
@Override
void makeSound() {
System.out.println(name + " meows");
}
}
// 다형성과 상속을 활용한 메서드 호출
public class Main {
public static void main(String[] args) {
Animal dog = new Dog("Buddy");
Animal cat = new Cat("Whiskers");
animalMakeSound(dog); // 다형성을 통해 메서드 호출
animalMakeSound(cat);
}
static void animalMakeSound(Animal animal) {
animal.makeSound(); // 다형성으로 실제 객체의 메서드 호출
}
}
6. 오버라이딩, 오버로딩의 개념과 차이
- 오버라이딩(Overrriding)
- 부모 클래스에게 상속받은 메서드를 재정의 하는 것
- 부모 매서드와 이름, 매개변수, 반환타입이 같아야 함
- 접근제어자는 부모클래스 보다 좁은 범위로 변경할 수 없음
- 부모 클래스의 메서드 보다 많은 수의 예외를 선언할 수 없음
- 오버로딩(Overloading)
- 이름은 같지만 기존에 없던 새로운 기능의 메서드를 새로 정의하는 것
- 하나의 클래스 안에 같은 이름의 메서드를 여러개 선언
- 다만 조건이 있음. 매개변수의 갯수나 데이터 타입이 달라야함
- 예시)
- println 메서드 : 오버로딩 하지 않고 사용하려면 매개변수의 데이터 타입마다 매서드 명이 달라야함
- 계산기 : 연산을 수행하려면 대입될 수 있는 매개변수의 모든 데이터 타입에 따라 메서드를 생성해야 함
7. 추상클래스, 인터페이스의 개념과 차이
- 추상 클래스 (Abstract Class)
- 하나 이상의 추상 메서드를 포함하는 클래스
- 추상 메서드는 선언만 되어 있고 구현 내용이 없기 때문에 하위 클래스에서 반드시 구현해야함
- 모든 추상 메서드를 구현하지 않는 경우 추상클래스로 간주
- 추상 클래스는 추상 메서드 뿐만 아니라 일반적인 멤버 변수와 메서드도 포함 가능
- 추상 클래스는 인스턴스화할 수 없음. 즉, 추상 클래스로 직접 객체를 생성할 수 없음. 추상 클래스를 상속은 하위 클래스에서 추상 메서드를 구현하여 객체를 생성해야함
- abstract 키워드를 사용하여 정의
- 접근 제어자의 제한이 없음
- 인터페이스 (Interface)
- 추상 메서드로만 구성되어 있고 클래스가 특정한 동작을 수행할 수 있도록 규격을 정의하는 역할
- 인터페이스를 구현하는 클래스는 인터페이스의 모든 추상 메서드를 반드시 구현해야함
- 모든 추상 메서드를 구현하지 않는 경우 인터페이스로 간주
- 인스턴스화 불가능. 하위 클래스에서도 불가능 하나 익명객체 사용하여 가능
- implements 키워드를 사용하여 정의
- 접근 제어자는 public만 가능함
- 차이점
- 추상 클래스는 상속 관계를 통해 코드의 중복을 방지하고 확장하기 위해 사용하고, 인터페이스는 클래스 간에 공통된 동작을 보장하기 위해 사용
- 추상 클래스는 단일 상속만 가능 / 인터페이스는 다중 구현이 가능
- 추상 클래스는 구현 코드와 추상 메서드를 함께 포함할 수 있음 / 인터페이스는 Java 8 이전에 추상 메서드만을 가질 수 있었으나 Java 8부터는 디폴트, 정적 메서드 사용 가능
- 추상 클래스의 경우 하위 클래스는 상위 클래스의 멤버 변수와 메서드를 직접 사용할 수 있음 / 인터페이스는 상수와 메서드 시그니처만을 제공하며, 실제 구현은 클래스에서 이루어짐
- 추상 클래스와 인터페이스를 혼합하여 사용할 수 있음
8. 동기화, 비동기화의 개념과 차이
- 동기화 (Synchronization)
- 여러 개의 작업이나 스레드가 동시에 실행될 때, 공유 데이터나 리소스에 대한 접근을 조절하여 데이터 일관성과 정확성을 유지하는 것
- 동기화를 통해 한 작업이 공유 데이터를 변경하는 동안 다른 작업이 이를 읽거나 수정하지 못하도록 보호할 수 있음
- 동기화의 목적
- 경쟁 조건 (Race Condition) 방지 : 여러 개의 작업이 동시에 공유 데이터에 접근하면서 예측할 수 없는 결과가 발생하는 문제를 방지
- 데이터 일관성 유지 : 공유 데이터의 변경과 관련된 작업들이 순차적으로 실행되어 데이터 일관성을 유지
- 비동기화 (Asynchronization)
- 여러 작업이 순차적으로 실행되는 대신, 각 작업이 독립적으로 실행되고 완료 여부를 기다리지 않고 다음 작업을 시작
- 비동기 프로그래밍은 주로 비동기 이벤트 핸들링, 멀티스레드 환경, 네트워크 통신 등에서 사용
- 비동기화의 특징
- 작업 간 독립성 : 각 작업은 다른 작업에 영향을 미치지 않고 별도로 실행
- 작업 큐 및 콜백 : 작업은 큐에 추가되어 순차적으로 실행되며, 작업이 완료되면 콜백 함수를 호출하여 결과를 처리
- 차이점
- 동기화는 작업이 순차적으로 실행되도록 조절하여 데이터의 일관성을 유지 / 비동기화는 작업이 병렬 또는 별개의 스레드에서 독립적으로 실행되며, 작업 간 상호작용이 필요할 때 콜백 등을 활용하여 처리
- 동기화는 여러 스레드나 작업 간의 실행을 조절하는 방식이므로 상대적으로 더 많은 리소스와 오버헤드가 발생할 수 있음 / 비동기화는 병렬 실행과 작업 분산으로 인해 효율성이 증가
- 동기화는 복잡한 상호작용과 데이터 일관성을 필요로 하는 경우에 적합 / 비동기화는 작업 간 독립성이 중요하거나, 대기 시간을 최소화하려는 경우에 적합
- ex) 웹 애플리케이션에서 사용자의 요청을 처리할 때 동기화된 방식으로 데이터베이스에 접근하여 데이터 일관성을 유지할 수 있음 / 웹 애플리케이션에서 비동기적으로 이미지 업로드나 이메일 발송과 같은 작업을 처리하면 응답 시간을 단축시킬 수 있음