Home > Backend > Java > ☕️[Java] 자바란?

☕️[Java] 자바란?
Java Programming Language Backend

자바란?

자바 언어 특징.

1. 타 언어에 비해 배우기 쉽습니다.

2. 플랫폼에 독립적입니다.

- 자바 언어가 플랫폼에 독립적인 이유는 그 설계 철학과 메커니즘에 근거합니다.
- 자바는 "한 번 작성하면, 어디서든 실행된다(Write Once, Run Anywhere, WORA)" 라는 철학을 실현하기 위해 개발되었습니다.
    - 이를 가능하게 하는 핵심 요소는 자바 가상 머신(Java Virtual Machine, JVM)과 자바 바이트코드의 도입입니다.

자바의 플랫폼 독립성의 주요 요인

1. 자바 가상 머선(JVM)

  • JVM은 자바 바이트 코드를 실행할 수 있는 런타임 환경을 제공합니다. 자바 프로그램이 컴파일되면, 플랫폼에 독립적인 바이트코드로 변환됩니다.
    • 이 바이트코드는 어떤 특정 하드웨어나 운영 체제의 기계어 코드가 아닌, JVM이 이해할 수 있는 중간 형태의 코드입니다.
  • JVM은 바이트코드를 받아 각 플랫폼에 맞는 기계어 코드로 변환하고 실행합니다.
    • 따라서, 자바 애플리케이션은 다양한 운영 체제에서 JVM만 설치되어 있으면 실행될 수 있습니다.
      2. 컴파일과 실행의 분리
  • 자바 프로그램은 소스 코드(.java 파일)에서 바이트코드(.class 파일)로 컴파일되는 과정과, 실행 시 바이트 코드가 실제로 실행되는 과정으로 나누어집니다. 이 두 단계의 분리는 프로그램을 한 번 컴파일하면, 그 컴파일된 코드가 다양한 환경의 JVM에서 실행될 수 있게 합니다.
    3. 표준화된 API
  • 자바는 풍부하고 표준화된 API를 제공합니다. 이 API들은 플랫폼에 관계없이 일관된 방식으로 작동하므로, 개발자는 운영 체제의 특징을 신경 쓰지 않고도 애플리케이션을 개발할 수 있습니다. 예를 들어, 파일 시스템 접근, 네트워크 프로그래밍 등의 기능은 모든 플랫폼에서 동일한 자바 코드로 작동합니다.
    4. 언어와 라이브러리의 독립성
  • 자바 언어와 표준 라이브러리는 플랫폼에 특화된 구현으로부터 독립적입니다.
    • 즉, 자바의 표준 라이브러리 구현은 다양한 하드웨어와 운영 체제에서 동일하게 작동하도록 설계되었습니다.

3. 객체지향 프로그래밍입니다.

  • 객체지향 프로그래밍?
    • 자바에서의 객체지향 프로그래밍(Object-Oriented Programming, OOP)은 소프트웨어를 설계하고 구현할 때 객체라는 개념을 중심으로 프로그래밍하는 방식을 말합니다.
      • 객체지향 프로그래밍은 코드의 재사용성, 확장성 및 관리 용이성을 높이는 데 도움이 됩니다.
      • 자바는 객체지향 언어의 특징을 강하게 반영하고 있으며, 다음과 같은 기본 원칙에 따라 프로그래밍 됩니다.
        • 1. 캡슐화(Encapsulation)
          • 객체의 데이터(속성)와 그 데이터를 조작하는 메소드를 하나의 단위로 묶는 것을 말합니다.
          • 캡슐화를 사용하면 객체의 세부 구현 내용을 외부에서 알 필요 없이 객체가 제공하는 기능만을 사용할 수 있으며, 이는 코드의 유지보수를 용이하게 합니다.
        • 2. 상속(Inheritance)
          • 한 클래스가 다른 클래스의 특성을 상속 받아 사용할 수 있게 하는 것입니다.
          • 이를 통해(상속을 통해) 기존 코드를 재사용하면서 확장할 수 있고, 코드의 중복을 줄이며 유지 보수가 쉬워집니다.
        • 3. 다형성(Polymorphism)
          • 같은 이름의 메소드가 다른 작업을 수행할 수 있도록 하여 메소드의 오버라이딩(Overriding)이나 오버로딩(Overloading)을 가능하게 합니다.
            • 오버라이딩(Overriding) : 자식 클래스가 상속 받은 부모 클래스의 메소드를 재정의 하는 행위를 말합니다. 오버라이딩을 통해 자식 클래스는 상속 받은 메소드와 동일한 시그니처(메소드 이름, 매개변수 리스트)를 가지지만, 그 내용을 자신의 특정한 요구에 맞게 새롭게 구현할 수 있습니다. 오버라이딩된 메소드는 실행 시 다형성을 활용하여 해당 객체의 실제 타입에 따라 적절한 메소드가 호출됩니다.
              • 예시 코드
                java class Animal { void display() { System.out.println("This is an animal."); } } class Cat extends Animal { @Override void display() { System.out.println("This is a cat.") } }
            • 오버로딩(Overloading) : 같은 클래스 내에서 같은 이름의 메소드를 여러 개 정의할 수 있도록 하지만, 매개변수의 타입, 개수 또는 순서가 달라야 합니다. 이를 통해 메소드에 다양한 입력 파라미터를 제공할 수 있으며, 프로그래머가 같은 동작을 하는 메소드에 대해 다양한 옵션을 제공할 수 있습니다. 오버로딩은 컴파일 시간에 결정되며, 메소드 호출 시 전달된 매개변수에 따라 적절한 메소드가 선택됩니다.
              • 예시 코드
                      class Display {
                          void show(int a) {
                              System.out.println("Number: " + a);
                          }
                                                    
                          void show(String a) {
                              System.out.println("String: " + a);
                          }
                                                    
                          void show(int a, int b) {
                              System.out.println("Two numbers: " + a + ", " + b);
                          }
                      }
                
              • 이처럼 오버라이딩과 오버로딩은 자바 프로그래밍에서 메소드의 기능을 확장하거나 변경할 때 유용하게 쓰이는 기법입니다.
              • 오버라이딩은 주로 다형성을 활용한 동적 바인딩을 목적으로 하며, 오버로딩은 같은 이름의 메소드에 여러 입력 형태를 제공하기 위해 사용됩니다.
        • 4. 추상화(Abstraction)
          • 복잡한 실제 상황을 단순화하는 과정에서 중요한 특징만을 추출하여 프로그램 코드에 반영하는 것을 의미합니다.
          • 추상 클래스와 인터페이스를 통해 구현될 수 있습니다.
            • 이러한 원칙들은 자바를 사용하여 복잡한 시스템을 개발할 때 코드의 모듈화를 가능하게 하고, 이로 인해 대규모 소프트웨어 개발과 프로젝트 관리가 용이해집니다.
              • 코드의 모듈화(Modularization): 큰 프로그램을 작은 세부 모듈로 나누는 프로세스를 의미합니다. 이러한 모듈은 각각 독립적인 기능을 수행하며, 전체 시스템의 한 부분으로 기능합니다. 모듈화의 주요 목적은 프로그램의 관리를 용이하게 하고, 개발을 효율적으로 만들며, 코드의 재사용성을 높이는 것입니다.
              • 모듈화의 주요 이점은 다음과 같습니다.
                • 1. 유지보수성
                  • 모듈화된 코드는 각 모듈이 분리되어 있기 때문에, 하나의 모듈에서 발생한 문제가 다른 모듈에 미치는 영향을 최소화할 수 있습니다. 따라서 개별 모듈을 독립적으로 수정, 업데이트, 테스트할 수 있어 전체 코드베이스의 유지보수가 더 쉬워 집니다.
                    • 모듈(Module): 소프트웨어 설계에서 사용되는 기본 개념 중 하나로, 관련된 기능들을 논리적으로 그룹화하고 독립적으로 사용할 수 있는 코드의 단위를 의미합니다. 모듈은 프로그램의 특정 기능을 담당하며, 독립적인 개발, 테스트, 재사용이 가능하도록 설계됩니다. 모듈화된 코드는 대체로 명확하고 관리하기 쉬운 구조를 갖습니다.
                    • 모듈의 특징으로는 다음과 같습니다.
                      • 1. 독립성
                        • 모듈은 가능한 한 다른 모듈과 독립적으로 동작할 수 있어야 하며, 이를 통해 시스템의 복잡성을 줄이고, 각 모듈의 재사용성을 높일 수 있습니다.
                      • 2. 캡슐화
                        • 모듈은 자신의 구현 세부사항을 숨기고, 필요한 기능만을 외부에 제공하는 인터페이스를 통해 상호작용합니다. 이로 인해 모듈 간의 상호 의존성이 줄어들고, 변경 관리가 용이해집니다.
                      • 3. 인터페이스
                        • 모듈은 정의된 인터페이스를 통해 외부와 통신합니다. 인터페이스는 모듈이 제공하는 기능과 해당 기능을 어떻게 접근할 수 있는지를 명시합니다.
                    • 모듈의 예로는 다음으로 들 수 있습니다.
                      • 라이브러리
                        • 특정 기능을 제공하는 함수나 데이터 구조를 모아 놓은 코드 집합. 예를 들어, 수학 연산을 위한 수학 라이브러리, 데이터베이스 작업을 위한 데이터베이스 접근 라이브러리 등이 있습니다.
                      • 클래스
                        • 객체지향 프로그래밍에서 클래스는 속성(데이터)과 메소드(함수)를 캡슐화하여 모듈을 형성합니다. 클래스는 독립적으로 사용될 수 있으며, 다른 클래스와 상호작용할 수 있습니다.
                      • 패키지
                        • 관련된 여러 클래스나 모듈을 하나의 더 큰 단위로 그룹화한 것 입니다. 예를 들어, Java에서는 java.util 패키지가 여러 유틸리티 클래스와 인터페이스를 제공합니다.
                          • 모듈은 개발 과정을 체계화하고, 코드의 재사용성을 증가시키며, 유지 관리를 용이하게 하는 중요한 역할을 합니다.
                          • 모듈은 크기가 클 수도 있고 작을 수도 있으며, 프로젝트의 요구와 설계에 따라 그 범위와 기능이 결정됩니다.
                • 2. 재사용성
                  • 잘 설계된 모듈은 다른 프로그램에서도 재사용할 수 있습니다. 이는 소프트웨어 개발 시간과 비용을 줄이는 데 도움이 되며, 일관된 기능을 여러 프로젝트에 걸쳐 사용할 수 있습니다.
                • 3. 확장성
                  • 모듈화는 시스템의 확장성을 향상시킵니다. 새로운 기능이 필요할 때 기존 모듈을 수정하거나 새로운 모듈을 추가하기가 더 쉬워집니다. 이는 시스템의 유연성을 증가시키고, 변화하는 요구사항에 더 잘 대응할 수 있게 합니다.
                • 4. 가독성
                  • 작은 모듈로 나뉘어진 코드는 각각의 모듈이 명확한 기능을 수행하기 때문에, 전체 코드의 구조를 이해하기가 더 쉽습니다. 개발자가 프로그램의 특정 부분만을 이해하고도 효과적으로 작업할 수 있습니다.
                • 5. 팀 협업 향상
                  • 모듈화는 여러 개발자가 동시에 다른 모듈에서 작업할 수 있게 함으로써 팀 작업을 용이하게 합니다. 각 팀원이 특정 모듈에 집중할 수 있으며, 전체 프로젝트에 대한 의존성을 줄이면서 협업을 효율적으로 진행할 수 있습니다.
                    • 이처럼 코드의 모듈화는 소프트웨어 개발 과정에서 중요한 역할을 하며, 특히 대규모 프로젝트나 복잡한 시스템 개발에 있어 필수적인 접근 방식입니다.

4. Garbage Collector로 사용되지 않는 메모리를 자동적으로 정리해줍니다.

  • Garbage Collector(GC): 프로그램이 동적으로 할당한 메모리 영역 중에서 더 이상 사용하지 않는 부분을 자동으로 찾아서 해제하는 시스템을 말합니다. 이 과정을 통해 프로그램에서 발생할 수 있는 메모리 누수를 방지하고, 사용 가능한 메모리 리소스를 최적화합니다.
    • 프로그램이 동적으로 할당한 메모리 영역 : 프로그램 실행 중에 필요에 따라 할당되고 해제되는 메모리를 말합니다. 이는 프로그램의 런타임 중에 사용자의 요구나 데이터의 양에 따라 변화하는 메모리 요구 사항을 수용하기 위해 사용됩니다. 동적 메모리 할당은 프로그램이 시작할 때 필요한 메모리 양을 미리 알 수 없는 경우나, 실행 도중에 메모리 사용량이 변할 때 유용합니다.
      • 동적 메모리 할당의 특징은 아래와 같습니다.
        • 1. 유연성 : 동적 메모리 할당은 프로그램 실행 중에 필요한 메모리 크기를 조정할 수 있게 해줍니다. 이로 인해 프로그램은 사용자의 입력, 파일 크기, 또는 다른 실행 시 요소들에 따라 메모리 사용을 최적화할 수 있습니다.
        • 2. 효율성 : 필요할 때만 메모리를 할당하고, 더 이상 사용하지 않는 메모리를 해제함으로써 시스템 리소스를 보다 효율적으로 사용할 수 있습니다.
        • 3. 메모리 관리 : 동적 메모리는 일반적으로 힙(Heap) 영역에서 관리됩니다. 힙은 프로그램의 데이터 영역 중 하나로, 동적으로 할당되는 객체와 데이터에 사용됩니다. 힙 영역의 크기는 프로그램 실행 도중에 확장되거나 축소될 수 있습니다.
          • 동적 메모리 할당의 예는 다음과 같습니다.
            • 자바에서는 new 키워드를 사용하여 객체를 생성할 때 동적 메모리 할당이 일어납니다. 예를 들어, new ArrayList() 를 호출하면, 자바 런타입은 필요한 메모리를 힙에서 할당하여 ArrayList 객체를 저장합니다.
            • 객체 사용이 끝나면 자바의 GC가 더 이상 참조되지 않는 객체가 사용하던 메모리를 자동으로 해제합니다.
              • 동적 메모리 할당은 프로그램이 더 유연하고 효율적으로 동작하도록 돕지만, 관리가 제대로 이루어지지 않을 경우 메모리 누수나 성능 저하 같은 문제를 초래할 수 있습니다. 따라서 프로그래머는 동적 메모리 관리를 신중하게 수행해야 합니다.
          • GC의 주요 기능은 다음과 같습니다.
            • 1. 메모리 관리 자동화 : 프로그래머가 메모리 할당 및 해제를 직접 관리하는 대신 자바 런타입이 이를 자동으로 처리합니다. 이로 인해 개발자는 메모리 관리에 신경 쓰지 않고, 애플리케이션 로직 개발에 더 집중할 수 있습니다.
            • 2. 메모리 누수 방지 : GC는 참조되지 않는 객체들을 정기적으로 청소하여 메모리 누수를 방지합니다. 객체가 더 이상 필요 없을 때 자동으로 메모리에서 제거됩니다.
            • 3. 효율적인 메모리 사용 : 사용되지 않는 객체들을 정리함으로써 메모리를 효율적으로 사용하고, 애플리케이션의 성능을 유지할 수 있도록 도와줍니다.
          • GC의 작동 원리는 다음과 같습니다.
            • GC은 크게 두 단계로 진행됩니다.
              • 1. 객체 탐지 : GC는 더 이상 어떤 객체에도 참조되지 않는 객체들을 탐지합니다. 이러한 객체들은 프로그램에서 더 이상 사용되지 않는 것으로 간주됩니다.
              • 2. 메모리 회수 : 탐지된 객체들이 차지하고 있는 메모리를 해제합니다. 이 메모리는 다시 사용 가능한 상태가 되어, 새로운 객체를 위해 재할당될 수 있습니다.
          • GC 알고리즘
            • 자바는 다양한 GC 알고리즘을 제공합니다. 대표적인 몇 가지는 다음과 같습니다.
              • Mark-and-Sweep : 사용 중인 객체를 “표시(mark)”하고, 표시되지 않은 객체를 “쓸어내는(sweep)” 방식입니다.
              • Generational GC : 객체를 세대별로 분류하여, 생성된지 얼마 되지 않은 객체들(Young Generation)과 오래된 객체들(Old Generation)을 다르게 관리합니다. 이 방식은 대부분의 객체가 생성 후 짧은 시간 내에 소멸된다는 관찰에 기반합니다.
              • Compacting : 사용 중인 객체들을 메모리의 한쪽으로 몰아넣어(Compact), 메모리의 연속성을 높이고, 메모리 단편화를 방지합니다.
                • GC은 메모리 관리를 자동화하지만, 때로는 성능 저하를 일으킬 수 있습니다. 특히 GC가 실행되는 동안에는 프로그램의 다른 모든 작업이 일시적으로 중단(Stopping the world)될 수 있기 때문에, GC 동작 방식과 설정을 잘 이해하고 조절하는 것이 중요합니다.

JVM(Java Virtual Machine)

JVM은 자바 애플리케이션을 실행하기 위한 가상 머신으로, 자바 바이트코드를 로컬 기계 코드로 변환하여 실행하는 역할을 합니다.

  • 자바 바이트코드(Java Bytecode) : 자바 소스 코드가 컴파일된 후의 중간 형태입니다.
    • 자바 소스 파일(.java 파일)을 자바 컴파일러가 컴파일하면, 결과적으로 생성되는 것이 .class 파일로 저장되는 자바 바이트코드입니다.
    • 이 바이트코드는 기계어 코드는 아니지만, CPU가 직접 실행할 수는 없고, JVM이 이해하고 실행할 수 있는 명령어 세트로 구성되어 있습니다.
    • 바이트코드는 플랫폼에 독립적이기 때문에, 한 번 컴파일된 자바 프로램은 어떤 JVM이 설치된 시스템에서든 실행할 수 있습니다.
      • 이는 자바의 “한 번 작성하면, 어디서든 실행된다”라는 이점을 제공합니다.
  • 로컬 기계 코드(Local Machine Code) : 로컬 기계 코드는 특정 하드웨어 플랫폼의 CPU가 직접 이해하고 실행할 수 있는 명령어 코드입니다.
    • 이 코드는 플랫폼에 종속적이며, 다양한 운영 체제와 하드웨어 아키텍처는 각각의 기계어 코드를 가지고 있습니다.
    • 자바 바이트코드는 JVM을 통해 실행될 때, 두 가지 방법 중 하나로 실행될 수 있습니다.
      • 1. 인터프리터 : JVM은 바이트코드를 한 줄씩 읽고, 각 명령을 로컬 기계 코드로 변환하면서 실행합니다. 이 방법은 간단하지만, 실행 속도가 느릴 수 있습니다.
      • 2. JIT 컴파일러(Just-In-Time Compiler) : 이 방식에서는 JVM이 바이트코드 전체 또는 핵심 부분을 분석하여, 실행 전에 전체 코드를 로컬 기계 코드로 한번에 변환합니다. 이렇게 하면 프로그램의 실행 속도가 크게 향상됩니다.
        • 결국, 자바 바이트코드는 플랫폼 독립적인 중간 코드로서의 역할을 하며, 로컬 기계 코드는 실제 하드웨어에서 실행되기 위한 최종적인 코드 형태입니다. 이 두 코드의 변환과 실행은 JVM 내에서 처리되며, 사용자는 이 과정을 명시적으로 관리할 필요가 없습니다. 이것이 자바가 제공하는 큰 이점 중 하나입니다.

JVM은 자바의 “한 번 작성하면, 어디서든 실행된다(Write Once, Run Anywhere, WORA)” 라는 철학을 가능하게 하는 중요한 구성 요소입니다.

JVM 덕분에 자바 애플리케이션은 운영 체제나 하드웨어 플랫폼에 구애받지 않고 동일하게 실행될 수 있습니다.

JVM의 주요 기능.

1. 플랫폼 독립성 : 자바 프로그램은 JVM 위에서 실행되므로, JVM이 설치되어 있는 모든 운영 체제에서 같은 자바 프로그램을 실행할 수 있습니다.

  • 이는 JVM이 플랫폼에 특화된 코드로 바이트 코드를 변환하기 때문입니다.

2. 메모리 관리 : JVM은 자동 메모리 관리 기능을 제공합니다. 이는 GC를 통해 메모리 할당과 해제를 관리하여, 프로그래머가 메모리 누수 없이 효율적인 메모리 사용을 할 수 있도록 돕습니다.

3. 보안 : 자바 바이트코드는 JVM에 의해 검증되며 실행되기 전에 다양한 검사를 통해 안전성이 확보됩니다.

  • 이는 악의적인 코드 실행과 시스템 오류를 방지하는 데 도움이 됩니다.

4. 실행 환경 : JVM은 자바 애플리케이션에 필요한 실행 환경을 제공합니다.

  • 이 환경은 클래스 로더, 바이트코드 실행 엔진, 쓰레드 관리 등을 포함합니다.

JVM의 구성 요소.

1. 클래스 로더(Class Loader) : 클래스 파일들을 읽고 바이트코드를 JVM 메모리에 로드하는 역할을 합니다.
2. 실행 엔진(Excution Engine) : 로드된 클래스 파일의 바이트코드를 실행합니다. 이 엔진은 바이트코드를 해것하거나 필요에 따라 JTI(Just-In-Time) 컴파일러를 사용하여 바이트코드를 직접 기계 코드로 변환하여 실행 속도를 높일 수 있습니다.
3. 가비지 컬렉터(Garbage Collector) : JVM이 사용하지 않는 메모리 자원을 자동으로 회수합니다.
4. 메모리(Runtime Data Area) : JVM은 프로그램 실행을 위해 필요한 다양한 메모리 영역을 관리합니다. 이는 힙(Heap), 스택(Stack), 메소드 영역(Method Area), 프로그램 카운터(Program Counter) 등이 포함됩니다.