[CS] Java

Myung A Lee·2023년 7월 8일
0

CS

목록 보기
7/11
post-thumbnail

JAVA


Java

  • Java는 객체지향 언어
  • JVM을 통해 어떤 운영체제에서도 동일하게 실행 (운영체제에 독립적)
  • 가비지 컬렉터에 의해 자동으로 메모리를 관리
  • 다양한 오픈소스 라이브러리와 API를 지원
  • 멀티 스레딩 지원

JVM (Java Virtaul Machine 자바 가상 머신)

  • JVM은 컴파일된 Java 클래스 파일(bytecode)를 다양한 OS 환경에서 실행 가능하게 해준다.
  • JVM과 실행과정
  • 실행 절차
    • .java 파일 생성 → compiler: .java → *.class 파일인 Byte code로 Compile → JVM: Byte Code → 기계어인 Binary Code로 변환 → CPU에서 실행되어 사용자에게 제공
    • Class Loader: JVM 내 메모리 영역으로로 Class 파일을 로드하고 링크
    • Excution Engine: 메모리에 적재된 클래스들을 기계어로 변경해 실행
      - 인터프리터: 바이트 코드를 한줄씩 읽어 기계어로 변환
      - JIT 컴파일러: 반복되는 코드를 기계어로 변환해 캐싱 (-> 캐싱한 것을 바로 실행해 속도 향상)
  • Garbage Collection 수행 (메모리 관리)

Garbage Collection 가비지 컬렉션

  • 가비지 컬렉션(Garbage Collection)은 Java 프로세스에서 더 이상 사용하지 않는 메모리를 자동으로 해제 해주는 JVM의 작업
  • Java Runtime시 Heap 영역에 저장되는 객체들은 따로 정리하지 않으면 계속헤서 쌓이게되어 OutOfMemmory Exception(OOME)이 발생할 수 있다. 이를 방지하기 위하여 JVM에서는 주기적으로 사용하지 않는 객체를 수집하여 정리하는 GC를 진행한다.

Runtime Data Area (Java의 메모리 영역)

  • 모든 자바 프로그램은 자바 가상 머신(JVM)을 통해서 실행된다. 자바 프로그램이 실행되면, JVM은 운영 체제로부터 해당 프로그램을 수행할 수 있도록 필요한 메모리를 할당받는다. 이렇게 할당받은 메모리를 JVM은 메모리 공간을 효율성을 높이기 위해 메모리 공간을 용도에 따라 다음과 같이 여러 영역으로 구분하여 나누서 관리한다.

  • Runtime Data Area

  • Method Area 메서드 영역

    • JVM이 시작할 때 생성되고 모든 스레드가 공유하는 영역
    • 메소드 영역에는 코드에서 사용되는 클래스들을 클래스 로더로 읽어 클래스별로 정적 필드와 상수, 메서드 코드, 생성자 코드 등을 분류해서 저장한다.
  • Heap Area 힙 영역

    • 객체와 배열이 생성되는 영역
    • 여기에 생성된 객체와 배열은 JVM 스택 영의 변수나 다른 객체의 필드에서 참조한다. 만일 참조하는 변수나 필드가 없다면 의미 없는 객체가 된기 때문에 JVM에서는 이것을 쓰레기로 취급하고 Garbage Collector를 실행시켜 자동으로 제거한다.
  • JVM Stack JVM 스택

    • 메서드를 호출할 때마다 프레임을 추가하고 메서드가 종료되면 해당 프레임을 제거하는 동작을 수행
    • 프레임 내부에는 로컬 변수 스택이 있는데 기본 타입 변수와 참조 타입 변수가 추가 되거나 제거 된다. 스택 영역에 변수가 생성되는 시점은 초기화가 될 때 , 즉 최초로 변수에 값이 저장 될 때며 변수는 선언된 블록안에서만 스택에 존재하고 블록을 벗어나면 스택에서 제거된다.

OOP (Object Oriented Programming : 객체 지향 프로그래밍)

  • 자바는 객체 지향 프로그래밍을 지원한다. 데이터를 객체로 취급하여 객체 내부에 선언된 메서드를 활용하는 방식으로 캡슐화, 상속, 다형성, 추상화의 특징을 갖고 코드의 모듈화, 재사용성, 유지 보수성을 높이는데 도움이 된다.

Encapsulation 캡슐화

  • 서로 연관있는 속성과 기능들을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것으로 객체는 내부의 세부 구현을 숨기고 필요한 인터페이스만 노출하여 사용자가 공개된 정보에만 접근이 가능하고(Information Hiding) 객체를 쉽게 이해하고 사용할 수 있게 한다.
  • Getter/Setter
    필드는 외부에서 접근할 수 없도록 막고(private) 메소드는 공개해서 외부에서 메소드를 통해 필드에서 접근하도록 유도한다. 필드의 값을 외부로 리턴해주는 메소드를 Getter라고 하고 외부에서 갑슬 받아 필드를 변경하는 메소드를 Setter라고 한다.
  • Access modifiers 접근 제어자
    • 변수나 메서드의 사용 권한은 다음과 같은 접근 제어자를 사용하여 설정할수 있다.
    • 클래스와 인터페이스를 다른 패키지에서 사용하지 못하도록 막을 때 사용하며 객체 생성을 막기 위해 생성자를 호출하지 못하게 하거나 필드나 메소드를 사용하지 못하도록 막아야 하는 경우에도 사용한다.
    • 접근제어자 구분

Inheritance 상속

  • 클래스들 간에 상속 관계를 형성할 수 있다. 부모 클래스의 특성과 동작을 자식 클래스가 상속받아 재사용 할 수 있으며, 새로운 기능을 추가하거나 기존 기능을 변경할 수 있다. 코드의 재사용 측면, 계층적인 관계 생성, 유지 보수성 측면에서 중요하다.

Polymorphism 다형성

  • 다형성은 사용 방법은 동일하지만 다양한 객체를 이용해서 다양한 실행 결과가 나오도록 하는 것으로 대표적으로 오버로딩과 오버라이딩이 있다.
  • Overloading 오버로딩
    같은 이름을 가진 메서드를 여러개 두는 것을 의미한다. 메서드 타입, 매개변수 유형, 개수등으로 여러개를 둘 수 있다.
  • Overriding 오버라이딩
    주로 메서드 오버라이딩을 말하며 상위 클래스로부터 상속받은 메서드를 하위 클래스가 재정의 하는 것을 미한다. 매개 변수를 달리하는 생성자를 여러개 선언하는 것은 생성자 오버라이딩이라고 한다.
  • Overloading vs Overriding
    오버라이딩은 상속 관계에서 부모 클래스의 메서드를 자식 클래스에서 재정의 하는 것이고, 오버로딩은 같은 클래스 내에서 메서드 이름은 동일하지만 매개변수 목록이 다른 여러 개의 메서드를 정의하는 것이다. 이 두개념을 활용하여 다형성을 구현하고 유연하고 확장가능한 코드를 작성할 수 있다.

Abstraction 추상화

  • 구체적인 사물들의 공통적인 특징을 파악해서 이를 하나의 개념(집합)으로 다루는 것으로 복잡한 시스템으로부터 핵심적인 개념 또는 기능을 간추려 구체적인 클래스를 정의할 수 있다.
  • Abstract Class 추상 클래스
    • 하위 클래스에서 구현될 추상적인 기능이 포함된 클래스로 공통된 필드와 메소드의 이름을 통일할 목적으로 사용
    • 추상 클래스는 선언 시 abstract 키워드를 붙여야만 하며 객체화가 불가능하고 상속을 통해서만 자식 클래스를 만들 수 있다.
  • Abstract method 추상 메소드
    • 추상 클래스 내에 정의되는 선언 부분만 있고 구현 부분이 없는 메소드로 추상 클래스를 상속한 하위 클래스에서 구현을 강제화하여 재정의(Overriding)을 하도록 한다.
    • 공통적인 필드와 메소드를 추상 클래스에 모두 선언해두고 다른 점만 실체 클래스에 선언하면 작성 시간을 절약할 수 있다.
  • Interface 인터페이스
    • 인터페이스는 개발 코드와 객체가 서로 통신하는 접점 역할을 하며 인터페이스는 추상 메소드와 상수만 가진다.
    • 개발 코드가 직접 객체의 메소드르 호출하는 대신 중간에 인터페이스를 두는 이유는 개발 코드를 수정하지 않고 사용하는 개체를 변경 할 수 있도록 하기 위해서 이다. 인터페이스는 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하는냐에 따라서 실행 내용과 리턴값이 다를 수 있다.
  • Abstract Class vs Interface
    추상 클래스와 인터페이스는 선언만 있고 구현 내용이 없다는 점과 자식 클래스가 메서드의 구체적인 동작을 구현하도록 하여 결합도를 낮춘다는 공통점이 있지만 그 목적이 다르다는 차이가 있다. 추상 클래스는 메서드 오버라이딩을 통한 기능의 구현과 확장에 목적을 둔다면 인터페이스는 필수적으로 동작되어야 할 기능의 규격을 잡아주는 역할을 한다. 또한 추상클래스의 경우 다중 상속이 불가능하지만 인터페이스는 다중 구현이 가능하다.

Instance 멤버와 Static 멤버

  • Instance 멤버
    • 객체를 생성한 후 사용할 수 있는 필드와 메서드를 말하는데 이들을 각각 인스턴스 필드, 인스턴스 메소드라고 한다.
    • 객체가 사라지면 멤버도 사라진다.
  • Static 멤버
    • 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메서드를 말한다.
    • 정적 필드와 정적 메서드라고 부르며 선언 시 static 키워드를 추가적으로 붙이면 된다.
    • 멤버는 프로그램이 종료될 때 사라진다.
  • 자주 변하지 않는 값이나 공통으로 사용되는 값 같은 공용자원에 대한 접근에 있어서 매번 메모리에 로딩하거나 값을 읽어들이는 것보다 전역변수와 같은 개념을 통해 접근하는 것이 비용도 줄이고 효율을 높일 수 있다.

Java의 final 키워드 (final/finally/finalize)

  • final
    • 변수나 메서드 또는 클래스가 '변경 불가능'하도록 만든다.
    • static final (상수)
    • final 필드 vs Constant Value
      final 필드는 한 번 초기화 되면 수정할 수 없는 필드이지만 상수라고 부르지는 않는다. 불변의 값인 상수는 객체마다 저장할 필요가 없는 공용성을 띠고 있으며 여러가지 값으로 초기화 될 수 없기 때문이다. final 필드는 객체마다 저장되고 생성자의 매개값을 통해서 여러가지 값을 가질 수 있기 때문에 상수가 될 수 없다.

불변의 값을 저장하는 필드를 자바에서는 상수라고 부르며 static final 로 선언한다.

  • finally
    • try/catch 블록이 종료될 때 항상 실행될 코드 블록을 정의하기 위해 사용한다.
    • finally는 선택적으로 try 혹은 catch 블록 뒤에 정의할 때 사용한다.
    • finally 블록은 예외가 발생하더라도 항상 실행된다.
      • 단, JVM이 try 블록 실행 중에 종료되는 경우는 제외한다.
  • finalize()
    • Garbage Collector가 더 이상의 참조가 존재하지 않는 객체를 메모리에서 삭제하겠다고 결정하는 순간 호출된다.
    • Object 클래스의 finalize() 메서드를 오버라이드해서 맞춤별 GC를 정의할 수 있다.
      • protected void finalize() throws Throwable { // 파일 닫기, 자원 반환 등등 }

Error & Exception

  • Error
    실행 중 일어날 수 있는 치명적 오류, UncheckedException의 일종

  • Exception

    • 경미한 오류, try-catch를 이용해 프로그램의 비정상 종료를 막을 수 있음
    • CheckedException: 실행하기 전에 예측 가능한 예외, 반드시 예외 처리
    • UncheckedException: 실행하고 난 후에 알 수 있는 예외, 따로 예외처리를 하지 않음(예: RuntimeException)
  • Exception 일반 예외
    컴파일러 체크 예외라고도 하며 프로그램 실행 시 예외가 발생할 가능성이 높기 때문에 자바 소스를 컴파일하는 과정에서 해당 예외 처리 코드가 있는지 검사한다. 만약 예외 처리 코드가 없다면 컴파일 오류가 발생한다.

  • Runtime Exception 실행 예외
    컴파일러 넌 체크 예외라고도 하며 실행 시 예측할 수 없이 갑자기 발생하기 때문에 컴파일하는 과정에서 예외 처리 코드가 있는지 검사하지 않는다. 오로지 개발자의 경험에 의해서 예외 처리 코드를 작성해야한다.

  • Exception Class 예외 클래스
    자바에서는 예외를 클래스로 관리한다. JVM은 프로그램을 실행하는 도중에 예외가 발생하면 해당 예외 클래스로 객체를 생성하고 예외 처리 코드에서 예외 객체를 이용할 수 있도록 해준다. 모든 예외 클래스는 java.lang.Exception 클래스를 상속 받는다.

    • NullPointExeption : 자바 프로그램에서 가장 빈번하게 발생하는 실행 예외로 객체가 없는 상태에서 객체를 사용하려고 할 때 발생한다.
    • ArrayIndexOutOfBoundsException : 배열에서 인덱스 범위를 초과할 경우 발생한다.
    • NumberFormatException : 숫자로 변활 될 수 없는 문자가 포함되어 있을 때 발생한다.
    • ClassCastException : 클래스를 다른 타입으로 변환할 수 없을 때 발생한다.
  • 예외 처리 코드

    • try-catch-finally 블록 : 생성자 내부와 메소드 내부에서 작성되어 일반 예외와 실행 예외가 발생 할 경우 예외 처리를 할 수 있도록 해준다.
    • throws 키워드 : 메소드 선언부 끝에 작성되어 메소드에서 처리하지 않은 예외를 호출한 곳으로 떠넘겨 호출한 곳에서 예외 처리를 하도록 한다.

컬렉션 프레임워크

  • 데이터를 쉽고 효과적으로 관리할 수 있는 표준화된 방법을 제공하는 클래스의 집합

  • 주요 인터페이스로는 List, Set, Map이 존재한다.

  • List 컬렉션

    • 순서가 있는 데이터의 집합, 데이터 중복 허용
    • ArrayList, Vector, Linked List
  • Set 컬렉션

    • 순서가 없는 데이터의 집합, 데이터 중복 불허
    • HashSet, LinkedHashSet, TreeSet
  • Map 컬렉션

    • 키와 값이 한 쌍, 순서가 없는 데이터 집합, 키를 기준으로 중복 불허
    • HashMap, HashTable, LinkedHashMap, Properties, TreeMap

Generics 제너릭

  • 클래스 내부에서 지정하는 것이 아닌 외부에서 사용자에 의해 지정되는 것을 의미한다. 특정(Specific) 타입을 미리 지정해주는 것이 아닌 필요에 의해 지정할 수 있도록 하는 일반(Generic) 타입으로 데이터 형식 불일치 문제를 컴파일 과정에서 예방할 수 있다.

Thread 스레드

  • Thread
    프로세스 내에서 실행되는 흐름의 단위로 한 프로세스는 하나 이상의 스레드를 가지고 스레드를 동시에 실행할 수 있다. 이러한 실행 방식을 멀티스레드(multithread)라고 하며 자바는 이러한 멀티 스레드를 지원한다.
  • Process vs Thread
    • Process는 하나 이상의 Thread로 구성되어 각 Thread가 다양한 기능을 작동시킨다.
    • Process는 서로간에 Memory를 공유하지 않지만 Thread는 공유한다.
  • MultiThread Scheduling
    Thread는 여러 일을 동시에 주기적으로 처리 해 줄 수 있으나 제어가 어렵다는 문제가 있다. 기본적으로 Thread가 Round Robin 방식을 사용하기 때문이다. 즉, 먼저 시작했어도 더 늦게 끝날 수 있다. 이러한 Thread를 제어하기 위해 다양한 방법을 사용하고 있다.

Syncronous vs Asyncronous

  • Sychronized 동기
    • 한 자원에 동시에 접근하는 것 제한, 순차적으로 진행
    • Thread간 Memory Share가 가능하기 때문에 발생할 수 있는 데이터의 간섭,오염을 막을 때 사용
    • Java에서 synchronized 키워드 사용
    • Blocking 방식
  • Asyncronous 비동기
    • 현재 실행 중인 명령이 종료되지 않아도 다음 명령 실행 가능
    • ex. Ajax, Thread
    • Non-Blocking 방식

Serialization 직렬화

  • 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 시스템에서도 사용할 수 있도록 바이트(byte) 형태로 데이터 변환하는 기술(↔ 역직렬화)
  • 같은 자바 시스템에서의 데이터 전송, 저장에 최적화 되어있다.
  • SerialVersionUID
    • JVM: 직렬화와 역직렬화를 하는 시점의 클래스에 대한 버전 번호를 부여
    • 직렬화할 때의 버전 번호와 역직렬화를 할 때의 버전 번호가 다르면 역직렬화가 불가능하게 될 수 있기 때문에 이런 문제를 해결하기 위해 SerialVersionUID를 사용

Call by Reference & Call by Value

  • Call by Reference (참조에 의한 호출)
    • 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성, 인자로 전달되는 변수의 레퍼런스를 전달한다.
    • 따라서 함수 안에서 인자의 값이 변경되면, 인자로 전달된 변수의 값도 함께 변경된다.
  • Call by Value (값에 의한 호출)
    - 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성, 인자로 전달되는 변수의 값을 복사하여 함수의 인자로 전달한다.
    - 복사된 인자는 함수 안에서 지역적으로 사용되는 local value의 특성을 가진다.
    - 따라서 함수 안에서 인자의 값이 변경되어도, 외부의 변수의 값은 변경되지 않는다.
    다.
    - Java는 항상 Call by Value

String, StringBuilder, StringBuffer

  • String
    • 새로운 값을 할당할 때마다 새로 클래스에 대한 객체가 생성된다
    • String + String + String...
      • 각각의 String 주솟값이 Stack에 쌓이고, Garbage Collector가 호출되기 전까지 생성된 String 객체들은 Heap에 쌓이기 때문에 메모리 관리에 치명적이다.
    • String을 직접 더하는 것보다는 StringBuffer나 StringBuilder를 사용하는 것이 좋다.
  • StringBuilder & StringBuffer
    • memory에 append하는 방식으로, 클래스에 대한 객체를 직접 생성하지 않는다.
    • StringBuilder
      • 변경가능한 문자열
      • 비동기 처리
    • StringBuffer
      • 변경가능한 문자열
      • 동기 처리
      • multiple thread 환경에서 안전한 클래스(thread safe)

0개의 댓글