[2주차] Java의 문자열, 예외, 제네릭

goodstar·2024년 11월 4일
0

Java 스터디

목록 보기
4/14
post-thumbnail

String literal과 new String(””)의 차이

String literal : String Pool에 저장, 동일한 값을 공유, 메모리 효율성이 높다.
new String("") : 매번 Heap에 새 객체를 생성, 동일한 문자열이더라도 서로 다른 객체로 간주.

String literal ( String str = "hello"; )

  • String Pool에 저장: String literal은 String Pool이라는 메모리 영역에 저장됩니다. 동일한 문자열이 여러 번 선언되더라도 String Pool에 이미 존재하는 문자열 객체를 재사용하여 메모리 효율성을 높입니다.
  • 성능 효율성: String Pool을 사용하여 중복 문자열 객체 생성을 피하므로, 메모리 사용량이 적고 속도가 빠릅니다.

new String("") ( String str1 = new String("hello"); )

  • Heap 메모리에 저장: new String("hello")와 같이 new 키워드를 사용하여 문자열을 생성하면 Heap에 새로운 객체가 생성됩니다. 이는 String Pool을 사용하지 않기 때문에 매번 독립적인 객체가 만들어집니다.
  • 중복 객체 생성: 같은 문자열을 여러 번 생성하더라도 String Pool을 거치지 않아 매번 새로운 메모리 공간을 차지하게 됩니다.

이와 같이 String literal은 메모리 절약과 성능 이점을 가지며, new String("")은 매번 새로운 객체가 필요할 때 사용됩니다.

String, StringBuilder, StringBuffer의 차이점

String : 불변(immutable) 객체, 값이 변경될 때마다 새로운 객체를 생성
StringBuilder : 가변, thread-safe 하지않고, 내부 값을 직접 수정할 수 있다.
StringBuffer : 가변, thread-safe 하고, 내부 값을 직접 수정할 수 있다.

String

  • 불변성(immutable): String 객체는 한 번 생성되면 값이 변경될 수 없습니다. 문자열을 수정하는 작업이 발생할 때마다 새로운 String 객체가 생성됩니다.
  • 성능: 문자열을 자주 수정하면 새로운 객체가 계속 생성되어 메모리 사용량이 늘어나고 성능이 저하될 수 있습니다.
  • 사용 용도: 문자열을 한 번만 설정하고 변경할 필요가 없는 경우 적합합니다.

StringBuilder

  • 가변성(mutable): StringBuilder는 문자열이 자주 변경될 때 사용하기 적합하며, 내부 값을 직접 수정할 수 있습니다.
  • 스레드 안전성: 스레드 안전하지 않으므로, 단일 스레드 환경에서 사용해야 합니다.
  • 성능: 스레드 안전 검사를 하지 않으므로 StringBuffer보다 성능이 우수하여, 문자열의 빈번한 추가, 수정, 삭제가 필요할 때 유리합니다.

StringBuffer

  • 가변성(mutable): StringBuffer도 문자열을 변경할 수 있습니다.
  • 스레드 안전성: 각 메서드가 synchronized로 구현되어 스레드 안전을 보장합니다. 멀티스레드 환경에서 동일한 객체에 여러 스레드가 접근할 때 문제가 발생하지 않습니다.
  • 성능: 스레드 안전성을 보장하는 만큼 StringBuilder보다 다소 느릴 수 있습니다. 멀티스레드 환경에서 문자열 수정이 필요한 경우 적합합니다.

Exception과 Error의 차이

Exception : 프로그램에서 발생할 수 있는 예외적인 상황, 일반적으로 프로그램 내에서 처리할 수 있다.
Error : 시스템 환경에서 발생하는 치명적인 오류, 프로그램이 이를 처리하지 않고 종료되는 것이 일반적이다.

Exception

  • 정의: Exception은 프로그램의 논리적 오류나 예상 가능한 문제 상황을 나타내며, 예외 처리를 통해 프로그램 내에서 복구할 수 있습니다.
  • 처리 가능성: try-catch 구문을 사용해 잡아서 처리할 수 있으며, 프로그램이 정상적으로 계속 실행될 수 있도록 할 수 있습니다.

Error

  • 정의: Error는 프로그램 실행 중에 발생하는 심각한 시스템 수준의 오류로, 메모리 부족이나 JVM 내의 치명적인 문제 등, 개발자가 해결할 수 없는 문제를 나타냅니다. ( OutOfMemoryError , StackOverflowError )
  • 처리 가능성: 일반적으로 Error는 프로그램 내에서 처리하지 않고, 발생 시 프로그램이 종료되는 경우가 많습니다.

Exception 클래스의 예시

주요 예시로는 IOException, ClassNotFoundException, SQLException, NoSuchMethodException, NullPointerException 등이 있습니다.

IOException : 입출력 작업 중 발생하는 예외입니다. ( 파일을 읽으려 할 때 파일이 존재하지 않으면 FileNotFoundException이 발생 )

import java.io.*;

public class FileNotFoundExceptionExample {
    public static void main(String[] args) {
        try {
            // 없는 파일을 읽으려 시도하여 FileNotFoundException 발생
            FileReader fileReader = new FileReader("nonexistentfile.txt");
        } catch (FileNotFoundException e) {
            System.out.println("FileNotFoundException 발생: 파일을 찾을 수 없습니다. (" + e.getMessage() + ")");
        } catch (IOException e) {
            System.out.println("IOException 발생: " + e.getMessage());
        }
    }
}

ClassNotFoundException : 특정 클래스를 로드하려 할 때 해당 클래스가 클래스패스에 없을 경우 발생하는 예외입니다.
SQLException: 데이터베이스와의 통신에서 발생하는 예외로, SQL 문법 오류, 연결 실패, 권한 문제, 잘못된 쿼리 실행 등의 이유로 발생합니다.

import java.sql.*;

public class SQLExceptionExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://invalid_host:3306/test";
        try {
            Connection connection = DriverManager.getConnection(url, "user", "password");
        } catch (SQLException e) {
            System.out.println("SQLException 발생: " + e.getMessage());
        }
    }
}

NoSuchMethodException : 호출하려는 메서드가 클래스에 존재하지 않을 때 발생하는 예외입니다.
NullPointerException: null 값을 참조하려 할 때 발생하는 예외입니다. 초기화되지 않은 객체의 메서드 호출, 필드 접근 등에서 발생합니다.

public class NullPointerExceptionExample {
    public static void main(String[] args) {
        String str = null;
        try {
            System.out.println(str.length());
        } catch (NullPointerException e) {
            System.out.println("NullPointerException 발생: " + e.getMessage());
        }
    }
}

Checked Exception과 Unchecked Exception의 차이

Checked Exception : 컴파일 시점에 예외 처리가 요구되는 예외
Unchecked Exception : 런타임 시에만 발생하며 예외 처리가 강제되지 않는 예외

Checked Exception

  • 정의: 컴파일 시점에 반드시 예외 처리가 필요하다고 요구되는 예외입니다.
  • 특징: try-catch로 예외를 처리하거나 메서드에 throws 키워드를 통해 호출자에게 예외를 넘기지 않으면 컴파일 에러가 발생합니다.
  • 주요 예시: IOException, SQLException, ClassNotFoundException 등 파일 입출력, 데이터베이스 연결, 클래스 로딩과 같이 예측 가능한 오류가 발생할 수 있는 작업에 주로 사용됩니다.
  • 사용 목적: Checked Exception은 외부 자원(파일, 네트워크, 데이터베이스 등)과의 상호작용 중 발생할 수 있는 예외를 미리 처리하도록 강제하여 프로그램의 안정성을 높입니다.

Unchecked Exception

  • 정의: 런타임 시 발생하며, 예외 처리가 강제되지 않는 예외입니다.
  • 특징: 필요에 따라 try-catch로 처리하거나 무시할 수 있습니다. 주로 프로그래밍 논리 오류로 인해 발생하는 예외들이 포함됩니다.
  • 주요 예시: NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException 등이 있으며, 프로그래밍 로직에 따라 발생할 수 있는 예외가 대부분입니다.
  • 사용 목적: Unchecked Exception은 개발자가 코드에서 예측하지 못한 논리적 오류나 잘못된 입력으로 인해 발생하는 상황을 나타냅니다. 예외 처리가 선택적입니다.

throw와 throws의 차이

throw : 예외를 실제로 발생시키는 데 사용
throws : 메서드가 던질 수 있는 예외를 명시하여 호출하는 코드에서 예외를 처리할 수 있도록 안내.

throw : 예외를 강제로 발생시킬 때 사용합니다. ( 예: throw new IllegalArgumentException("Invalid input") );

throws : 메서드 선언에서 예외가 발생할 가능성을 명시하는 것입니다. throws를 통해 호출자에게 예외 처리가 필요하다는 사실을 알리는 규약 역할 ( 예: public void readFile() throws IOException )
throws가 선언된 메서드를 호출하는 쪽에서는 해당 예외를 처리해야 합니다. throws를 통해 예외를 상위로 넘기면, 최종적으로 예외가 발생하는 곳에서만 하나의 try-catch로 묶어서 예외를 처리할 수 있습니다. 모든 메서드에서 개별적으로 처리하는 것이 아니고, 예외를 발생한 곳에서만 처리할 수 있어 불필요한 코드를 줄 일 수 있습니다.

try~catch~finally 구문에서 finally의 역할

finally : 예외 발생 여부와 관계없이 항상 실행. 주로 자원 해제나 마무리 작업(예: 파일 닫기, 데이터베이스 연결 종료 등)을 위해 사용.

finally 블록은 예외가 발생하든 하지 않든 무조건 실행됩니다. 예외가 발생하여 catch 블록이 실행된 경우에도, 예외가 발생하지 않아 catch를 건너뛸 때도 finally는 실행됩니다.
주로 파일, 데이터베이스 연결 등 외부 자원을 해제하거나, 마무리 작업을 수행할 때 사용됩니다. 항상 실행되므로 중요한 정리 작업을 안전하게 처리할 수 있습니다.

Throwable과 Exception의 차이

Throwable : Java 예외 계층 구조의 최상위 클래스로, 모든 예외와 오류를 포함.
Exception : Throwable의 하위 클래스 중 하나로, 주로 프로그램 내에서 발생하는 예외적 상황을 나타냅니다.

Throwable: 모든 에러와 예외의 최상위 클래스입니다. Java에서 예외 처리의 시작점이 되는 클래스이며, Throwable을 상속받는 클래스만이 throw 키워드로 던질 수 있습니다.
모든 예외와 오류를 포괄하기 때문에, 구체적인 예외나 오류보다는 전체적인 예외 처리 흐름을 다룰 때 사용됩니다.
Exception: Throwable의 하위 클래스 중 하나로, 프로그램 내에서 발생할 수 있는 예외적인 상황을 나타냅니다. 주로 프로그램의 로직과 관련된 예외를 처리합니다.

제네릭(Generic)이란 무엇이고, 왜 사용할까요?

제네릭(Generic) : 타입을 매개변수로 사용하여 코드의 타입 안전성과 재사용성을 높여주는 기능

제네릭은 Java에서 클래스나 메서드에 사용할 데이터 타입을 파라미터로 명시할 수 있는 기능입니다.

  • 타입 안전성 보장: 제네릭을 사용하면 컴파일 시점에 타입 검사가 이루어져, 잘못된 타입을 사용하는 코드에 대해 컴파일 에러가 발생합니다. 이는 ClassCastException과 같은 런타임 오류를 줄여주며, 코드의 안전성을 높입니다.
  • 코드 재사용성 향상: 제네릭을 사용하면 하나의 클래스나 메서드를 여러 타입에 대해 반복적으로 작성할 필요 없이 재사용할 수 있습니다. 예를 들어, List<Integer>List<String> 모두 같은 List 클래스를 사용하여 타입만 다르게 적용할 수 있습니다.
  • 명확한 코드: 제네릭을 통해 컬렉션이나 메서드가 사용할 타입을 명확하게 명시할 수 있어, 코드의 가독성이 높아집니다.

제네릭을 사용한 경험 소개

주문 관리 API를 개발하면서 제네릭을 활용하여 타입 안전성과 코드의 재사용성을 높이는 방식으로 설계하였습니다.

  • 타입 안정성을 높이기 위한 제네릭 활용 : 제네릭을 사용함으로써 컴파일 시점에 타입을 검사하여 안전한 코드를 작성할 수 있었습니다. 예를 들어, ResponseEntity<OrderResponseDto> 와 같이 ResponseEntity의 타입 파라미터로 DTO 객체를 지정하여, API 응답이 항상 OrderResponseDto 타입으로 리턴됨을 보장했습니다. 이를 통해 잘못된 타입으로 인한 런타임 오류 가능성을 미리 방지할 수 있었습니다.

  • 재사용성 향상 : List<OrderResponseDto> 와 같은 제네릭 리스트 타입을 사용하여 특정 데이터 타입을 반복적으로 작성할 필요 없이 코드의 재사용성을 높였습니다. 이를 통해 주문 관련 DTO 리스트를 일관된 타입으로 처리할 수 있어 코드 중복을 줄였고, 코드 유지보수가 용이해졌습니다.

0개의 댓글

관련 채용 정보