가비지 컬렉션(Garbage Collection)
자바 런타임 환경은 더 이상 사용되지 않는 객체를 자동으로 삭제합니다. 이 과정을 가비지 컬렉션(Garbage Collection)이라고 합니다. 객체에 대한 모든 참조가 없어지면 그 객체는 가비지 컬렉션 대상이 됩니다. 변수에 보관된 참조는 변수가 스코프(범위)를 벗어날 때 일반적으로 제거됩니다. 또는, 참조변수를 특별한 값인 null로 설정하여 객체 참조를 명시적으로 제거할 수도 있습니다. 자바 런타임 환경에는 가비지 컬렉터가 있어서 더 이상 참조되지 않는 객체가 사용하는 메모리를 주기적으로 해제합니다. 가비지 컬렉터는 적절한 시점이라고 판단되면 자동으로 작업을 수행합니다.
추상 클래스 (Abstract Class)
추상 클래스는 하나 이상의 추상 메서드를 포함할 수 있는 클래스입니다. 추상 클래스는 인스턴스화할 수 없으며, 다른 클래스가 상속받아야 합니다. 추상 클래스는 주로 공통된 메서드나 필드를 포함하여 상속받는 클래스들이 이를 공유할 수 있도록 합니다.
- 정의: 추상 클래스는 하나 이상의 추상 메서드를 포함할 수 있는 클래스입니다. 추상 클래스 자체는 인스턴스화할 수 없으며, 반드시 이를 상속받은 하위 클래스에서 추상 메서드를 구현해야 합니다.
- 용도: 클래스 계층 구조에서 공통적인 속성과 메서드를 정의하고, 구체적인 구현은 하위 클래스에 맡기고자 할 때 사용합니다.
- 구현: abstract 키워드를 사용하여 정의합니다.
abstract class Animal {
// 일반 메서드
public void eat() {
System.out.println("This animal eats food.");
}
// 추상 메서드 (구현 없음)
public abstract void makeSound();
}
class Dog extends Animal {
// 추상 메서드 구현
@Override
public void makeSound() {
System.out.println("Woof");
}
}
class Cat extends Animal {
// 추상 메서드 구현
@Override
public void makeSound() {
System.out.println("Meow");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.eat(); // This animal eats food.
myDog.makeSound(); // Woof
myCat.eat(); // This animal eats food.
myCat.makeSound(); // Meow
}
}
추상 메서드 (Abstract Method)
추상 메서드는 메서드 몸통이 없는 메서드입니다. 즉, 추상 메서드는 선언만 되어 있고, 구현은 되어 있지 않습니다. 추상 메서드를 포함하는 클래스는 반드시 추상 클래스여야 합니다. 추상 메서드는 상속받는 클래스에서 반드시 구현(override)해야 합니다.
- 정의: 추상 메서드는 구현부가 없는 메서드입니다. 즉, 메서드의 시그니처만 선언하고, 실제 구현은 하위 클래스에서 합니다.
- 용도: 공통적인 인터페이스를 정의하여 하위 클래스에서 반드시 특정 메서드를 구현하도록 강제하고자 할 때 사용합니다.
- 구현: 추상 클래스 내에서 abstract 키워드를 사용하여 선언합니다.
어노테이션(Annotation)
코드에 대한 메타데이터를 제공하는 데 사용되는 특수한 형태의 문법입니다. 어노테이션은 클래스, 메서드, 필드, 매개변수 등에 추가하여 추가적인 정보를 제공하거나 특정 동작을 지정할 수 있습니다. 자바의 애너테이션은 주석처럼 보이지만, 컴파일러나 런타임 환경에서 처리될 수 있는 유용한 정보를 포함하고 있습니다.
주요 용도
- 컴파일러 지시: 컴파일러에게 특정 행동을 하도록 지시할 수 있습니다. 예를 들어, @Override는 메서드가 슈퍼클래스의 메서드를 오버라이드하는 것임을 컴파일러에게 알려줍니다.
- 컴파일 타임 처리: 코드를 자동 생성하거나 수정하기 위한 도구로 사용될 수 있습니다. 예를 들어, Lombok 라이브러리는 애너테이션을 사용하여 자동으로 게터와 세터 메서드를 생성합니다.
- 런타임 처리: 런타임에 리플렉션을 통해 애너테이션 정보를 읽고 처리할 수 있습니다. 프레임워크(예: 스프링)에서는 애너테이션을 통해 구성 정보를 지정하거나 동작을 변경할 수 있습니다.
자바의 기본 애너테이션
자바는 여러 기본 애너테이션을 제공합니다:
- @Override: 메서드가 슈퍼클래스의 메서드를 오버라이드하는 것임을 나타냅니다.
- @Deprecated: 이 애너테이션이 붙은 요소는 더 이상 사용되지 않음을 나타냅니다.
- @SuppressWarnings: 특정 컴파일러 경고를 억제합니다.
- @FunctionalInterface: 해당 인터페이스가 함수형 인터페이스임을 명시합니다. 즉, 추상 메서드가 하나뿐인 인터페이스입니다.
- @SafeVarargs: 가변 인수 메서드(varargs)가 안전하게 사용됨을 보장합니다. Java 7부터 사용할 수 있습니다.
접근 제어자(access modifier)
자바에서 클래스 멤버(필드와 메서드)에 대한 접근 제어는 접근 제어자(access modifier)를 사용하여 이루어집니다. 접근 제어자는 클래스, 변수, 메서드, 생성자 등의 접근 범위를 제한하여 데이터 은닉을 구현하고, 코드의 안전성과 유지보수성을 높입니다. 자바에는 네 가지 주요 접근 제어자가 있습니다.
Top Level[class 지정, interface] 수준 : public 또는 default(package-private)[패키지 전용](명시적 수정자가 없음).
멤버 수준 : public, private, protected 또는 default(package-private)패키지 전용(명시적 수정자가 없음).
내부클래스는 외부클래스의 멤버이기 때문에 다 사용가능
1. public
- 정의: public으로 선언된 멤버는 모든 클래스에서 접근할 수 있습니다.
2. protected
- 정의: protected로 선언된 멤버는 같은 패키지 내의 클래스와 해당 클래스를 상속받은 하위 클래스에서 접근할 수 있습니다. 다른 패키지에서는 상속받은 클래스 내부에서 접근할 수 있습니다.
3. default (package-private)
- 정의: 접근 제어자를 명시하지 않으면 default 접근 수준이 됩니다. 이는 같은 패키지 내의 클래스에서만 접근할 수 있습니다.
4. private
- 정의: private으로 선언된 멤버는 해당 클래스 내에서만 접근할 수 있습니다.
스태틱 코드 블럭 (Static Code Block)
스태틱 코드 블럭은 클래스가 로드될 때 실행되는 블럭입니다. 주로 클래스 초기화 작업에 사용됩니다. 스태틱 블럭은 클래스가 메모리에 로드될 때 한 번 실행되며, 주로 스태틱 변수의 초기화에 사용됩니다.
class Example {
static {
// 스태틱 블럭 내용
System.out.println("Static block executed.");
}
public Example() {
System.out.println("Constructor executed.");
}
public static void main(String[] args) {
System.out.println("Main method started.");
Example ex1 = new Example();
Example ex2 = new Example();
}
}
스태틱 블럭의 실행 순서
위 코드를 실행하면 다음과 같은 출력이 발생합니다:
Static block executed.
Main method started.
Constructor executed.
Constructor executed.
- 스태틱 블럭은 클래스가 처음 로드될 때 한 번 실행됩니다.
- 그 후에 main 메서드가 실행됩니다.
- Example 클래스의 인스턴스를 생성할 때마다 생성자가 실행됩니다.
요약
- 스태틱 블럭은 클래스가 처음 로드될 때 한 번 실행됩니다.
- 주로 스태틱 변수의 초기화 또는 클래스 초기화 작업에 사용됩니다.
- 스태틱 블럭은 클래스 로딩 시 자동으로 실행되며, 생성자보다 먼저 실행됩니다.
- 복잡한 초기화 작업이 필요할 때 유용합니다.
array = 스트링클래스 객체 참조값을 저장하는 변수
String 클래스 객체를 참조하는 변수들을 배열에 저장하는 예제
public class StringArrayExample {
public static void main(String[] args) {
// String 객체들을 참조하는 변수들을 배열에 저장
String[] stringArray = new String[3];
// 배열의 각 요소에 String 객체를 할당
stringArray[0] = "Hello";
stringArray[1] = "World";
stringArray[2] = "Java";
// 배열의 요소를 출력
for (int i = 0; i < stringArray.length; i++) {
System.out.println("stringArray[" + i + "]: " + stringArray[i]);
}
// 배열의 요소를 변경
stringArray[1] = "OpenAI";
// 변경된 배열의 요소를 출력
for (int i = 0; i < stringArray.length; i++) {
System.out.println("stringArray[" + i + "]: " + stringArray[i]);
}
}
}
stringArray[0]: Hello
stringArray[1]: World
stringArray[2]: Java
stringArray[0]: Hello
stringArray[1]: OpenAI
stringArray[2]: Java
중첩 클래스(Nested Class)
다른 클래스 내부에 정의된 클래스를 말합니다. 중첩 클래스는 논리적으로 관련된 클래스를 그룹화하고, 외부 클래스와의 관계를 더 명확하게 할 수 있습니다. Java에서는 중첩 클래스를 네 가지 유형으로 분류할 수 있습니다:
- 정적 중첩 클래스(Static Nested Class)
- 내부 클래스(Inner Class)
- 지역 클래스(Local Class)
- 익명 클래스(Anonymous Class)
1. 정적 중첩 클래스 (Static Nested Class)
정적 중첩 클래스는 외부 클래스의 스태틱 멤버처럼 동작합니다. 정적 중첩 클래스는 외부 클래스의 인스턴스 멤버에 접근할 수 없으며, 스태틱 멤버에만 접근할 수 있습니다.
2. 내부 클래스 (Inner Class) ( 넌스태틱 네스티드 클래스(Non-static Nested Class) )
내부 클래스는 외부 클래스의 인스턴스 멤버로 동작하며, 외부 클래스의 모든 멤버(스태틱 멤버 포함)에 접근할 수 있습니다.
내부 클래스의 장점
- 캡슐화: 내부 클래스는 외부 클래스와 밀접하게 연관된 기능을 그룹화하여 캡슐화할 수 있습니다.
- 외부 클래스 멤버 접근: 내부 클래스는 외부 클래스의 인스턴스 변수와 메서드에 쉽게 접근할 수 있습니다.
3. 지역 클래스 (Local Class)
지역 클래스는 메서드 내부에 정의된 클래스로, 메서드 내에서만 사용할 수 있습니다. 지역 클래스는 메서드의 로컬 변수와 파라미터에 접근할 수 있습니다.
4. 익명 클래스 (Anonymous Class)
익명 클래스는 이름이 없는 중첩 클래스로, 일반적으로 일회성으로 사용됩니다. 익명 클래스는 클래스의 선언과 동시에 인스턴스를 생성합니다.
외부 클래스의 this 참조하기
내부 클래스에서 외부 클래스의 this를 참조하려면, 내부 클래스가 외부 클래스의 인스턴스를 가지고 있어야 합니다. 내부 클래스는 외부 클래스의 인스턴스와 밀접하게 연관되어 있으며, 내부 클래스의 인스턴스를 생성할 때 외부 클래스의 인스턴스를 참조할 수 있습니다.
외부 클래스의 this와의 차이점
내부 클래스에서 this 키워드는 내부 클래스의 인스턴스 자체를 가리킵니다. 따라서 내부 클래스의 메서드 내에서 this를 사용하면 내부 클래스의 인스턴스를 참조하게 됩니다.
this.new는 내부 클래스에서 외부 클래스의 인스턴스를 통해 새로운 내부 클래스의 인스턴스를 생성하는 방법을 나타냅니다. 이는 주로 내부 클래스에서 외부 클래스의 인스턴스를 통해 내부 클래스를 동적으로 생성할 때 사용됩니다.
public class OuterClass {
private String outerField = "Outer field";
class InnerClass {
private String innerField;
public InnerClass(String innerField) {
this.innerField = innerField;
}
public void displayFields() {
System.out.println("Outer field: " + outerField);
System.out.println("Inner field: " + innerField);
}
}
public void createInnerInstance(String innerField) {
// 외부 클래스의 인스턴스를 통해 내부 클래스의 새로운 인스턴스 생성
InnerClass inner = this.new InnerClass(innerField);
inner.displayFields();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.createInnerInstance("Inner value");
}
}
설명
- OuterClass는 외부 클래스로, InnerClass는 OuterClass 내부에 정의된 내부 클래스입니다.
- InnerClass는 외부 클래스의 outerField와 innerField를 출력하는 displayFields 메서드를 가지고 있습니다.
- createInnerInstance 메서드는 외부 클래스의 인스턴스를 통해 내부 클래스의 새로운 인스턴스를 생성합니다. this.new InnerClass(innerField) 구문을 사용하여 외부 클래스의 인스턴스인 this를 기준으로 내부 클래스의 새로운 인스턴스를 생성합니다.
- main 메서드에서는 OuterClass의 인스턴스를 생성하고, createInnerInstance 메서드를 호출하여 내부 클래스의 인스턴스를 생성하고 출력합니다.
주의사항
- this.new는 내부 클래스의 인스턴스를 생성할 때 외부 클래스의 인스턴스를 기반으로 하며, 일반적으로 내부 클래스의 생성자를 호출할 때 사용됩니다.
- 내부 클래스는 외부 클래스의 인스턴스에 종속적이므로, 외부 클래스의 인스턴스가 없는 상황에서는 내부 클래스의 인스턴스도 생성할 수 없습니다.
- this.new는 내부 클래스의 생성 시점을 외부 클래스의 인스턴스와 맞추어주는 역할을 합니다.
이를 통해 this.new가 내부 클래스에서 외부 클래스의 인스턴스를 통해 내부 클래스를 생성하는 방식임을 이해할 수 있습니다.
Iterator 인터페이스의 주요 메서드
hasNext()
hasNext() 메서드는 이터레이터가 다음 요소를 가지고 있는지 여부를 확인하는 메서드입니다. 이 메서드는 boolean 타입을 반환하며, 다음 요소가 있으면 true를 반환하고, 없으면 false를 반환합니다.
public interface Iterator<E> {
boolean hasNext();
E next();
// ...
}
next()
next() 메서드는 이터레이터에서 다음 요소를 반환하는 메서드입니다. 이 메서드는 제네릭 타입 E를 반환하며, 다음 요소가 없을 경우 NoSuchElementException 예외를 발생시킵니다. 따라서 hasNext()를 사용하여 다음 요소의 존재 여부를 먼저 확인한 후에 next()를 호출하는 것이 좋습니다.
랩핑 클래스(Wrapper Class)는 프리미티브 데이터 타입을 객체로 감싸는 클래스를 말합니다. 자바에서는 다음과 같은 원시 데이터 타입에 대한 랩핑 클래스가 있습니다:
- Boolean: boolean 타입의 래퍼 클래스
- Byte: byte 타입의 래퍼 클래스
- Short: short 타입의 래퍼 클래스
- Integer: int 타입의 래퍼 클래스
- Long: long 타입의 래퍼 클래스
- Float: float 타입의 래퍼 클래스
- Double: double 타입의 래퍼 클래스
- Character: char 타입의 래퍼 클래스
이 래퍼 클래스들은 각각의 원시 데이터 타입을 객체로 변환하여 다양한 기능을 제공하거나, 제네릭 코드에서 사용할 수 있도록 합니다.
public class WrapperExample {
public static void main(String[] args) {
// 원시 데이터 타입을 래퍼 클래스로 변환
Integer intObj = new Integer(42);
Double doubleObj = new Double(3.14);
// 래퍼 클래스에서 원시 데이터 타입으로 변환
int intValue = intObj.intValue();
double doubleValue = doubleObj.doubleValue();
System.out.println("Integer value: " + intValue);
System.out.println("Double value: " + doubleValue);
}
}