devseop08 님의 블로그

[Basic] 자바 기본 개념 개요 본문

Language/Java

[Basic] 자바 기본 개념 개요

devseop08 2025. 7. 18. 11:46
  • 변수와 타입
    • 변수는 데이터가 저장되는 메모리 공간이고, 변수의 형태, 즉 메모리 공간의 형태를 메모리 공간 타입, 변수의 타입이라 한다.
    • 자바는 정수, 실수, 문자, 논리값 데이터를 저장할 수 있는 메모리 공간의 기본 타입을 제공한다.
    • 정수 타입 : byte, char, short, int, long
      • byte
        • 크기 : 8비트, 1바이트
        • 값의 저장 범위: -2^7 -1 ~ 2^7 -1
      • short
        • 크기 : 16비트, 2바이트
        • 값의 저장 범위: -2^15-1 ~ 2^15-1
      • char => 유니코드(문자)에 해당하는 이진수 데이터들을 저장할 수 있다.
        • 크기 : 16비트, 2바이트
        • 값의 저장 범위 : 0~ 2^16-1
      • int
        • 크기 : 4바이트
        • 값의 저장 범위: -2^31-1 ~ 2^31-1
      • long
        • 크기: 8 바이트
        • 값의 저장 범위: -2^63-1 ~ 2^63-1
    • 실수 타입 : float, double
      • float
        • 크기 : 4바이트
        • 유효숫자(정밀도): 정규화 표현식 소수점 이하 7자리 이내
        • 값의 저장 범위: -3.4e38 ~ 3.4e38
        • float 자료형 메모리에 저장되는 실수 리터럴은 끝에 f를 붙여준다.
          • 15.87f
      • double
        • 크기 : 8바이트
        • 유효숫자(정밀도): 정규화 표현식 소수점 이하 15자리 이내
        • 값의 저장 범위: -1.79e308 ~ 1.79e308
        • double 자료형 메모리에 저장되는 실수 리터럴은 끝에 lf를 붙여준다.
    • 문자 타입 : char
      • char => 유니코드(문자)에 해당하는 이진수 데이터들을 저장할 수 있다.
        • 크기 : 16비트, 2바이트
        • 값의 저장 범위 : 0~ 2^16-1
    • 논리 타입 : boolean
    • 상수 리터럴과 타입 변환
      • 리터럴 : 그 자체로 값을 의미하는 것
      • 정수 리터럴
      • 실수 리터럴
      • 문자 리터럴
        • 자바는 유니코드를 지원하기 때문에 한글 한글자도 문자 리터럴 하나로 취급할 수 있게됐다.
        • '가', '나', '각', '난', 'ㄱ', 'ㄴ' 과 같이 한글 낱말 하나 하나를 문자 리터럴로 쓸 수 있는 것이다.
        • C언어에선 이것이 불가했다. 아스키코드만을 지원하기 때문에 알파벳 하나는 문자 하나로 취급하지만, 한글 한글자는 문자열로 취급하는 것이 C언어이다.
      • 논리 리터럴
        • true, false => 1비트의 크기로 컴파일 true => 1 , false => 0
      • 상수 리터럴의 타입 변환 연산
        • 상수 리터럴은 아직 타입이 정해지지 않았고, 자신의 크기에 맞는 가장 공간을 적게 차지하는 적합한 메모리 공간 타입의 비트 수를 차지한 형태이다.
        • 정수 리터럴은 기본적으로 자신의 데이터를 표현할 수 있는 만큼 메모리 공간 타입의 비트 수를 차지한 형태이지만, 정수 리터럴 끝에 L기호를 붙여주면 long 타입 메모리 공간만큼의 크기로 비트 수를 차지한다.
        • 실수 리터럴은 기본적으로 double 타입 메모리 공간만큼의 비트 수를 차지하지만, 실수 리터럴 끝에 f를 붙여주면 float 타입 메모리 공간만큼의 비트 수를 차지하게 된다.
        • 상수 리터럴이 대입 연산을 통해 변수에 저장될 때 해당 변수의 메모리 공간 타입의 값 저장 범위 밖에 해당한다면, 이 경우엔 자동 타입 변환이 발생하지 않기 때문에 상수 리터럴에 대해 명시적으로 타입 변환 연산을 수행해줘야 한다. 그렇지 않으면 컴파일 에러 발생
        • byte b = 10; 가능, byte b = 128; 불가능 => byte b = (byte)128;
        • char c = 6000 가능, char c = -1; 불가능 => char c = (char)-1;
      • 변수의 타입 변환 연산
        • 변수는 메모리 공간 타입이 정해진 형태이다.
        • 어떤 변수에 대해서 대입 연산을 통해 다른 메모리 공간 타입의 변수에 저장된 값을 저장하려 할 때, 좌항 변수 타입의 값 저장 범위가 우항 변수 타입의 값 저장 범위를 포함하지 못하면, 이 땐 자동 타입 변환이 일어나지 않기 때문에, 좌항 변수 타입에 맞춰 우항 변수의 메모리 공간 타입을 변환해줘야 한다. 그렇지 않으면, 컴파일 에러가 발생한다.
        • byte b = 20; char c;
        • c = b; 불가능, c = (char)b; 가능
        • byte 타입 혹은 short 타입으로 선언된 변수가 산술 연산(+, -, *, / )의 피연산자로 사용될 때, 해당 byte 타입 혹은 short 타입 변수는 int 타입으로 자동 형변환된다.
    • 변수와 시스템 입출력
      • 출력
        • println(상수 리터럴 또는 변수) => 인수 내용을 그대로 출력하고 개행
        • print(상수 리터럴 또는 변수) => 인수 내용을 그대로 출력
        • printf(변환 문자를 포함한 문자열, 상수 리터럴 또는 변수)
          인수 내용을 문자 변환 형식에 맞게 출력
      • 입력
        • Sytem.in.read() => 입력된 인코딩 데이터를 변환하지 않고 인코딩된 데이터 숫자 그대로 입력
        • 키보드에서 입력받은 데이터를 문자열 형식으로 반환, 저장
    Scanner scanner = new Scanner(System.in);

    String str = scanner.nextLine();// 키보드 입력마다 들어온 인코딩 데이터들을 문자                                           형식으로 변화하여 이것들을 모아 문자열로 반환    
  • 연산자
  • 조건문
  • 반복문
  • 분기문
  • 참조 타입(발전된 포인터랄까?...)
    • 기본 타입과 참조 타입
      • 자바의 타입은 기본 타입과 참조 타입으로 분류된다.
      • 기본 타입 : 정수, 실수, 문자, 논리 리터럴을 저장하는 메모리 공간의 타입
      • 참조 타입 : 배열, 클래스, 인터페이스, 열거 타입으로 힙 메모리 영역에 생성된 "객체"(구현 객체)의 메모리 상 저장 주소값을 저장하는 메모리 공간의 타입,
    • 메모리 사용 영역
      • JVM이라는 하나의 프로세스가 운영 체제에서 할당받은 메모리 영역
        • 메소드 영역과 힙 영역 JVM 스택 영역으로 분류된다.
        • 메소드 영역(Method Area)
          • JVM이 시작할 때 생성되고 모든 스레드가 공유하는 영역!!!
          • 코드에서 사용되는 클래스들을 클래스 로더로 읽어,
          • 클래스별로 정적 필드(일반 필드 X)와 상수, 메소드 코드, 생성자 코드 등을 분류해서 저장
          • 자바(JVM) 클래스 로더와 클래스 로딩
            • 클래스 로더는 동적으로 클래스를 로딩한다, 즉 JVM의 런타임에 클래스를 로딩한다.
            • 동적 로딩 방식에는 로드 타임 동적 로딩과 런타임 동적 로딩이 있다.
              • 로드 타임 동적 로딩 : 보통의 로딩 방식(정적 바인딩)
              • 런타임 동적 로딩(동적 바인딩)
                • 클래스 로드 타임에는 어떤 클래스가 쓰일 지 모르는 경우
                • ex) 메소드의 파라미터의 타입이 인터페이스 타입인 경우
                • 클래스 로드 타임에는 어떤 인터페이스 구현체의 주소값을 갖는 변수가 파라미터에 대입될 지 알 수 없다. 이는 런타임에 결정된다.
        • 힙 영역
          • 객체와 배열이 생성되는 영역
          • 생성된 객체와 배열은 JVM 스택 영역의 변수나 다른 객체의 필드에서 참조
          • 만약 참조하는 변수나 필드가 없다면 JVM이 이것을 쓰레기로 취급하고 쓰레기 수집기(가비지 컬렉터)를 실행시켜 자동으로 제거한다.
          • 자바는 코드로 객체를 직접 제거하는 방법을 제공하지 않는다.
          • 할당되는 메모리의 공간의 크기가 동적이다. 런타임에 정해진다.
          • 참조를 통해서 접근할 수 있는 영역
        • JVM 스택 영역
          • JVM 스택은 메소드를 호출할 때마다 프레임을 할당하고 메소드가 종료되면, 해당 프레임을 제거(pop)하는 동작을 수행한다.
          • 메소드 호출 시=>프레임 할당, 메소드 종료 시 프레임 제거
          • 프레임 내부에는 로컬 변수 스택이 있다.
          • 로컬 변수 스택에는 기본 타입 변수와 참조 타입 변수가 추가되거나 제거됨
          • 로컬 변수 스택에 변수가 생성되는 시점은 해당 변수가 초기화 될 때이고, 해당 변수는 자신이 속한 {}(블록)안에서만 로컬 변수 스택에 존재하며, 블록을 벗어나면 스택에서 제거된다.
          • 메소드가 종료되면, 프레임 내부의의 로컬 변수 스택도 제거되고, 그 안의 로컬 변수도 제거된다.
          • 할당되는 메모리의 크기가 고정적이다. 런타임 전부터 정해진다.
        • JVM 스택 영역 vs 힙 영역
          • JVM 스택 영역에 할당받을 메모리 공간의 크기는 실행 전부터, 즉 런타임 전부터 이미 결정돼있고 미리 알 수 있다.
          • JVM이 JVM 스택 영역으로 할당할 메모리 공간의 크기는 런타임 전부터 이미 정해져 있기 때문에 런타임에 JVM 스택 영역에 의한 메모리 단편화 문제가 발생하지 않을 것이다.
          • 하지만 객체를 생성한다 할 때, 이를 위해 힙 영역에 할당받을 메모리 공간의 크기는 실제로 객체 생성을 실제 실행해봐야 알 수 있기 때문에, 런타임에 메모리 단편화 문제가 발생할 가능성이 있다.
    • 참조 타입 변수 간의 !=, == 연산
      • 참조 타입 변수 간의 !=, == 연산은 동일한 주소값을 갖는지, 즉 동일한 주소에 저장된 객체를 참조하는지 알아볼 때 사용한다.
    • null과 NullPointerException
    • String 타입
      • 자바는 문자열을 하나의 객체로 취급한다.
      • String 타입은, 문자열에 해당하는 하나의 객체가 저장된 메모리 공간의 주소값을 저장하는 참조 타입의 메모리 공간 타입이다.
      • 자바는 같은 내용의 문자열 리터럴은 객체를 공유하도록 돼있다.
      • 반면에 new 연산자를 통해 생성된 문자열 객체는 같은 내용이라 하더라도 다른 객체로 취급한다. 즉 두 객체는 서로 다른 주소에 저장된다.
    • 배열
      • 배열 선언과 생성
        • char[] charArray;
        • charArray=new char[5];가능, charArray={'a', 'b', 'c', 'd' , 'e'};불가능
        • charArray=new char[]{'a', 'b', 'c', 'd' , 'e'};가능
        • Char[] charArray = new char[5]; 가능
        • Char[] charArray = {'a', 'b', 'c', 'd' , 'e'}; 가능
      • 배열 길이
        • charArray.length
      • 다차원 배열
      • 객체를 참조하는 배열
      • 배열 복사
        • System.arraycopy(Object src, int srcPos, Object dest, int destPos, int len)
    • 열거형
      • 클래스 로더는 열거 타입의 열거 상수들을 객체로 생성해서 힙 영역에 저장해두고 그것들이 저장된 메모리 공간의 주소값을 메소드 영역에서 해당 열거 타입 공간에 저장해둔다.
  • 클래스
    • 객체란?
    • 객체 지향
    • 필드
    • 생성자
      • 생성자는 객체가 생성될 때 객체의 초기화를 위해 실행되는 메소드이다.
      • 생성자는 객체가 생성되는 순간에 자동으로 호출되는 메소드이다.
      • 객체에 필요한 초기화를 실행하는 코드를 담아야 한다.
      • 객체 초기화란 필드를 초기화하거나 메소드를 호출해서 객체를 사용할 준비를 하는 것
      • new 연산자에 의해 생성자가 성공적으로 실행되면, 힙 영역에 객체가 생성되고 객체가 저장된 메모리 공간의 주소값이 리턴된다.
      • 리턴된 객체의 저장 주소는 클래스 변수에 저장된다.
      • 기본 생성자
        • 모든 클래스는 생성자가 반드시 존재하며, 생성자를 하나 이상 가질 수 있다.
        • 클래스 내부에 생성자 선언을 생략했다면, 컴파일러는 중괄호 내용이 비어있는 기본 생성자를 바이트 코드에 자동으로 추가한다.
        • 명시적으로 선언한 생성자가 1개라도 있으면 컴파일러는 기본 생성자를 바이트 코드에 추가하지 않는다.
        • 클래스가 public class로 선언됐다면, 기본 생성자도 public 접근 제한자로 선언된다.
      • 생성자 선언(명시적 선언)
        • 리턴 타입이 없고 클래스 이름과 동일하게 선언한다.
        • 클래스에 생성자가 명시적으로 선언되어 있을 경우엔, 반드시 선언된 생성자를 호출해서 객체를 생성해야만 한다.
        • 생성자가 명시적으로 선언된 경우엔, 기본 생성자를 호출할 수 없고 호출하면 컴파일 에러 발생한다.
      • 필드 초기화
        • 클래스로부터 객체가 생성될 때 필드는 기본 초기값으로 자동 설정
        • 만약 다른 값으로 초기화하고 싶다면, 두 가지 방법이 있다.
          • 필드를 선언할 때 초기값을 주는 방법 => 동일한 클래스로부터 생성되는 객체들은 모두 같은 초기값을 갖게 된다.
          • 생성자에서 초기값을 주는 방법
            • 객체 생성 시점에 외부에서 제공되는 다양한 값들로 초기화되어야 하는 경우, 생성자에 매개 변수를 선언
      • 생성자 오버로딩
        • 다양한 방식으로 객체를 초기화하기 위해선 생성자도 다양화될 필요가 있다.
        • 자바는 다양한 방식으로 객체를 초기화할 수 있도록 생성자 오버로딩을 제공
        • 생성자 오버로딩이란 매개변수를 달리하는 생성자를 여러 개 선언하는 것
        • 생성자가 오버로딩되어 있을 경우, new 연산자로 생성자를 호출할 때 제공되는 매개값의 타입과 수에 의해 생성자가 결정
      • 다른 생성자 호출 this()
        • 생성자 오버로딩이 많아질 경우, 생성자 간의 중복된 코드가 발생 가능
        • 중복되는 코드를 하나의 생성자에만 집중시켜 놓고 다른 생성자에서는 중복되는 코드를 집중시켜 놓은 생성자를 this([매개값..]) 형식으로 호출
        • this()는 반드시 생성자의 첫 줄에서만 허용된다.
    • 메소드
    • 인스턴스 멤버와 정적 멤버(static)
      • 클래스의 인스턴스 멤버와 this
        • 인스턴스 멤버란 객체를 생성한 후에 사용할 수 있는 필드와 메소드를 말한다.
        • 이들을 각각, 인스턴스 필드와 인스턴스 메소드라 한다.
        • 인스턴스 메소드의 코드는 실제로는 객체 안에 들어있는 것이 아니다.
        • 클래스의 메소드 코드는 JVM 메모리 영역 중 모든 스레드가 공유할 수 있는 메소드 영역에 위치하는 것이 기본인데,
        • 만약 클래스 메소드 코드 블록 안에서 인스턴스 멤버를 사용하면 객체를 생성한 후에 사용할 수 있기 때문에 인스턴스 메소드라 하는 것이다.
        • this
          • 객체 내부에서 인스턴스 멤버(인스턴스 필드와 인스턴스 메소드)를 접근할 때 사용, 객체가 생성된 후의 바로 그 해당 객체를 지칭한다.
      • 클래스의 정적 멤버와 static
        • 정적 멤버란 클래스에 고정된 멤버를 말하며, 객체를 생성하지 않고도 사용할 수 있다.
        • 인스턴스 멤버를 사용하지 않는 메소드에 static 예약어를 사용해 정적 메소드를 선언할 수 있으며, 정적 메소드 코드 안에는 정적 멤버만 사용 가능하고 인스턴스 멤버는 사용될 수 없다.
        • 객체가 생성되지 않아도 정적 메소드를 사용할 수 있어야 하기 때문에 this 또한 정적 메소드 코드 안에서 사용될 수 없다.
        • 클래스 로더가 클래스(바이트코드)를 로딩하여 JVM 메모리 영역 중 메소드 영역에 적재할 때 클래스 별로 정적 필드와 정적 메소드를 관리하므로 클래스 로딩이 끝나면 정적 필드와 정적 메소드를 사용할 수 있다.
      • 싱글톤
        • private 접근 제한자와 정적 멤버를 사용하는 클래스를 이용해 해당 클래스에 대해 프로그램 상의 모든 스레드가 하나의 객체만을 공유하도록 보장하는 싱글톤 객체를 만들 수 있다.
        • public class Singletone{ private static Singletone singletone = new Singletone(); private Singtone(){ } public static Singletone getInstance(){ return singltone; } }
      • final 필드와 상수(static final), 불변의 값
        • final 필드는 초기값이 저장되면, 이것이 최종적인 값이 되어서 프로그램 실행 도중에 수정할 수 없다는 뜻이다.
        • final 필드의 초기값을 줄 수 있는 방법은 두 가지
          • 필드 선언 시에 주는 법
          • 생성자에서 주는 법
          • 생성자는 final 필드의 최종 초기화를 마쳐야 한다. 만약 초기화 되지 않은 final 필드를 그대로 두면 컴파일 에러 발생
    • 패키지와 접근 제한자
      • 패키지의 물리적인 형태는 파일 시스템의 폴더이나, 단순히 파일 시스템의 폴더 기능만 하는 것이 아닌 클래스의 일부분으로 클래스를 유일하게 만들어주는 식별자 역할을 한다.
      • 클래스 이름이 같더라도 다른 패키지 있으면 다른 클래스이다.
      • package 선언
      • import 문
        • 사용하고자 하는 클래스가 다른 패키지 경로에 위치할 때 사용
      • 접근 제한
        • 클래스 접근 제한
          • default
            • public 생략 시 해당 클래스는 default 접근 제한을 갖는다.
            • 같은 패키지 안의 클래스에서는 해당 클래스에 대해 제한 없이 접근할 수 있다.
            • 다른 패키지 안의 클래스에서는 접근 불가
          • public
            • 다른 패키지 안의 클래스와 같은 패키지 안의 클래스 모두에서 해당 클래스에 접근 가능
        • 생성자 접근 제한
          • default
            • 같은 패키지의 클래스에서는 가능, 다른 패키지 클래스에서는 불가능
          • public
            • 같은 패키지의 클래스, 다른 패키지의 클래스에서 모두 가능
          • protected
            • 같은 패키지의 클래스에서는 가능, 다른 패키지라도, 해당 클래스를 상속받은 클래스라면 접근 가능
          • private
            • 오직 해당 클래스에서만 가능
        • 필드와 메소드 접근 제한
          • default
            • 같은 패키지의 클래스에서는 객체 생성을 통한 접근 가능, 다른 패키지 클래스에서는 접근 아예 불가능
          • public
            • 같은 패키지의 클래스, 다른 패키지의 클래스에서 모두 객체 생성을 통한 직접 접근 가능
          • protected
            • 같은 패키지의 클래스에선 protected 필드나 메소드가 속한 클래스의 객체 생성을 통한 직접 접근이 가능하고 , protected 필드나 메소드가 속한 클래스를 상속한 클래스의 객체 생성을 통한 직접 접근도 가능하다
            • 왜냐하면 protected 필드나 메소드가 속한 클래스를 상속한 클래스의 객체 생성이 일어나면 그 객체 안에 protected 필드나 메소드가 속한 클래스 자체의 객체도 생성되어 포함되기 때문에 같은 패키지의 클래스에서 protected 필드나 메소드가 속한 클래스의 객체 생성이 일어난 것과 같기 때문이다.
            • 또한 다른 패키지라도, protected 필드나 메소드가 속한 클래스를 상속받은 클래스라면, 클래스 내부에 한해
            • 상속받은 protected 필드나 메소드에 접근이 가능하다. 단, 다른 패키지에선 절대 protected 필드나 메소드가 속한 클래스의 새로운 객체 생성을 통한 직접 접근은 불가능하다!!!
          • private
            • 오직 해당 클래스에서만 가능
          •  
          • Getter와 Setter
            • 일반적으로 객체 지향 프로그래밍에서는 객체의 필드를 객체 외부에서 직접 있는 그대로 접근하여 읽고 쓰는 것을 막는다
            • 그 이유는 외부에서 객체의 필드를 있는 그대로 마음대로 읽거나 변경하는 경우 객체의 무결성이 깨질 수 있기 때문이다.
            • 따라서 클래스 필드의 접근 제한을 private로 두고, 객체의 무결성이 깨지지 않도록 필드값을 읽는 public 접근 제한 메소드 getter와 무결성이 깨지지 않도록 필드값을 변경하는 public 접근 제한 메소드 setter를 따로 선언하여 사용한다.
  • 상속
    • 상속은 이미 잘 만들어진 클래스를 재사용해서 새로운 클래스를 만들도록 하기 때문에 중복되는 코드를 줄여준다.
    • 상속을 이용하면 부모 클래스의 수정으로 모든 자식 클래스들도 수정되는 효과를 가져오기 때문에 클래스 코드의 유지 관리가 용이하다
    • 클래스 상속 규칙
      • extends
      • 여러 개의 부모 클래스를 상속할 수는 없다
      • private 접근 제한 멤버는 상속 대상에서 제외되며, 상속하려는 부모 클래스가 다른 패키지에 존재할 시, default 접근 제한 멤버도 상속 대상에서 제외된다.
    • 부모 생성자 호출
      • 자식 클래스의 생성자가 명시되지 않았다면, 컴파일러가 부모 생성자 super를 호출하는 코드가 담긴 자식 클래스의 기본 생성자를 자동으로 생성해준다. 부모 클래스의 기본 생성자가 존재해야 한다.
      • 명시적으로 자식 클래스의 생성자를 선언한 경우, 만약 해당 생성자 코드 블록 안에 super()가 생략돼 있으면, 컴파일러가 자동으로 super()를 생성자 코드의 제일 첫 번째 실행문으로 추가해주고 이는 부모 클래스의 기본 생성자이므로 부모 클래스에 기본 생성자가 선언돼있어야 한다.
      • 만약 자식 클래스의 생성자에서 명시적으로 부모 생성자를 호출할 시엔 이와 같은 형식의 부모 생성자가 부모 클래스 안에 선언돼 있어야 한다.
      • super()는 반드시 자식 클래스 생성자의 첫 줄에 위치해야 하며, 그렇지 않으면 컴파일 에러가 발생한다.
    • 메소드 재정의, 오버라이딩
      • 재정의 하려는 부모 클래스 메소드와 동일한 리턴 타입, 메소드명, 매개변수 타입과 개수로 재정의해야한다.
      • 더 강한 접근 제한자로 재정의 할 수는 없다.
      • 메소드가 재정의 되었다면, 자식 객체에서 해당 메소드를 호출하면 자식 메소드가 호출된다.
      • 오버라이딩된 메소드가 아닌 원래의 부모 메소드를 호출하고 싶다면 클래스 내부의 다른 메소드에서 super를 이용해 super.부모메소드()로 호출 가능하다.
    • final 클래스와 final 메소드
      • 각각 상속할 수 없고, 재정의 할 수 없다.
    • 타입 변환과 다형성
      • 클래스도 기본 타입과 마찬가지로 타입 변환이 있다.
      • 클래스의 변환은 상속 관계에 있는 클래스 간에 일어난다. 자식은 부모 타입으로 자동 타입 변환이 일어난다.
      • Child c = new Child(); Parent p = c; => Parent 클래스로 자동 타입 변환
      • Parent 클래스의 필드와 메소드만 접근 가능하지만, 특정 메소드가 자식 클래스에서 재정의 되었다면 해당 메소드는 재정의된 자식 클래스의 메소드로 호출된다. (동적 바인딩)
      • c = p; 불가능, c = (Child)p 가능
      • Child c = (Child)new Parent(); 불가능!! => 컴파일 에러 발생
      • 강제 타입 변환은 자식 객체가 부모 타입으로 변환되어 있는 상태에서 다시 자식 타입으로 변환하고자 할 때만 가능하다.
      • intance of 연산자 => 객체 instance of 타입 => 객체가 해당 타입으로 타입 변환이 가능한지 확인, true or false return
      • 어떤 클래스가 있다고 할 때, 해당 클래스의 멤버 필드들의 타입을 부모 클래스 타입으로 선언하면, 해당 필드들에 대한 타입 상속을 이용해 필드의 다형성을 구현할 수 있다.
      • 메소드의 매개변수 타입을 부모 클래스 타입으로 선언하는 경우도 마찬가지로 다형성을 구현할 수 있다.
    • 추상 클래스
      • 객체를 직접 생성할 수 있느 클래스를 실체 클래스라고 한다면, 이 실체 클래스들의 공통적인 특성을 추출해서 선언한 클래스를 추상클래스라고 한다.
      • 공통된 필드와 메소드의 이름을 통일할 목적
      • 실체 클래스를 작성할 때 시간 절약
      • 추상 클래스 선언
        • 클래스 선언에 abstract 키워드를 붙여야 한다.
        • abstract 키워드가 붙은 클래스는 new 연산자를 이용해 객체를 생성할 수 없고 상속을 통해 자식 클래스에 대한 객체만 만들 수 있다.
        • 추상 클래스도 일반 클래스와 마찬가지로 필드, 생성자, 메소드를 선언할 수 있다. 다만 new 연산자로 직접 생성자를 호출해 객체를 생성하지 못하고 상속을 통해 자식 클래스의 객체만 생성할 수 있을 뿐이다. 직접적인 생성자 호출 불가
        • 추상 클래스의 자식 클래스에 대한 객체를 상속할 때 부모 클래스인 추상 클래스의 생성자를 호출하므로 추상 클래스도 생성자는 있어야 한다.
        • 추상 메소드, abstract 키워드가 붙은 메소드 => 자식 클래스에서 반드시 재정의 해줘야 한다. 강제적이다.
        • abstract 키워드가 붙지 않은 메소드도 선언 가능하다.
  • 인터페이스
    • 인터페이스는 추상 메소드의 집합이다.
    • 인터페이스는 개발 코드와 객체가 서로 통신하는 접점 역할을 한다.
    • 개발 코드가 인터페이스의 메소드를 호출하면, 인터페이스는 객체의 메소드를 호출시키기 때문에 개발 코드는 객체의 내부 구조를 알 필요없이, 인터페이스의 메소드만 알고 있으면 된다.
    • 개발 코드에서 직접 객체의 메소드를 호출하지 않고, 중간에 인터페이스를 두는 이유는 개발 코드를 수정하지 않고, 사용하는 객체를 변경할 수 있도록 하기 위해서다.
    • 인터페이스는 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로, 어떤 객체를 사용하는냐에 따라 실행 내용과 리턴값이 달라질 수 있다.
    • 개발 코드 측면에서는 코드 변경 없이, 실행 내용과 리턴값을 다양화할 수 있다는 장점이 있다.
    • 인터페이스의 장점
      • 인터페이스는 두 객체 간의 '연결, 대화, 소통'을 돕는 '중간 역할'을 한다.
      • 선언과 구현을 분리시킬 수 있게 한다.
      • 개발 시간을 단축할 수 있다. 인터페이스의 추상 메소드 호출이 가능하며, 구현 클래스의 멤버 변수는 애초에 직접 접근이 불가하고 메소드를 통해서만 접근이 가능하기 때문에 구현 클래스를 정의하기 전에도 인터페이스만 가지고도 개발을 해놓을 수 있다.
      • 변경에 유리한 유연한 설계가 가능하다.
      • 표준화가 가능하다. ex) JDBC
      • 서로 관계없는 클래스들 간의 관계를 맺어줄 수 있다. 서로 다른 부모 클래스를 상속하는 클래스들 간의 공통성을 담은 하나의 인터페이스를 만들어서 서로 연관이 없는 클래스들을 모아줄 수 있다.
    • 인터페이스 선언
      • class 키워드 대신에 interface 키워드를 사용한다.
      • 인터페이스는 상수 필드와 추상 메소드, staic 메소드, default 메소드만을 구성 멤버로 갖는다. 인터페이스는 객체로 생성할 수 없기 때문에 생성자를 가질 수 없으며 인스턴스 또는 정적 필드를 선언할 수 없다.
      • 생성자 불가, 인스턴스 필드 불가, 정적 필드 불가
      • 상수 필드는 public static final로 선언하며, 생략 가능하다.
      • 추상 메소드는 모두 public abstract의 특성을 갖게 되며, 생략하더라도 컴파일 과정에서 자동으로 추가된다.
      • java 7까지, 인터페이스는 상수 필드와 추상 메소드만을 구성 멤버로 가졌지만,
        java 8 이후부터, 인터페이스는 상수 필드, 추상 메소드와 더불어 static 메소드와 default 메소드도 구성 멤버로 가질 수 있게 됐다.
      • 인터페이스의 static 메소드는 클래스의 static 메소드와 마찬가지로
        인터페이스.static 메소드()형식으로 호출 가능하며, 인터페이스의 구현체에서는 인터페이스의 static 메소드를 오버라이드 하지도 않고, 상속하지도 않는다.
      • 인터페이스의 default 메소드는 구현 객체로 호출 가능하다.
        인스턴스.default 메소드()
      • 인터페이스의 모든 구성 멤버는 public 접근 제한의 특성을 갖지만, static 메소드 멤버의 경우, java 9 이후에는 private static 도 지원해, 인터페이스 내부에서만 쓰이는 헬퍼 메소드를 숨길 수 있다.
    • 인터페이스 구현
      • 구현 클래스와 구현 객체
      • 구현 클래스에서 인터페이스의 추상 메소드들 중 일부만 구현을 한다면, 해당 구현 클래스는 abstract로 선언돼야 한다. 구현 클래스에서 구현되지 않은 추상 메소드는 여전히 public abstract로 선언된 추상 메소드이다.
      • 다중 인터페이스 구현 클래스
        • 다중 인터페이스를 구현한 클래스의 경우, 구현하는 인터페이스의 모든 추상 메소드에 대해 실제 메소드 코드를 작성해야 한다.
    • 인터페이스 사용
      • 클래스의 필드, 생성자 또는 메소드의 매개 변수, 메소드의 로컬 변수
    • 타입변환과 다형성
      • 부모 클래스의 상속을 이용해 필드의 다형성과 매개 변수의 다형성을 구현할 수 있었듯이, 인터페이스의 구현을 이용해 필드의 다형성과 매개 변수의 다형성을 구현할 수 있다
      • 자동 타입 변환
      • 필드의 다형성
      • 매개변수의 다형성
      • 강제 타입 변환
      • 객체 타입 확인
      • 인터페이스 상속
        • 인터페이스의 조상은 인터페이스만 가능하다.
        • 인터페이스는 다중 상속이 가능하다.
        • 인터페이스의 static 메소드는 상속(extends와 implements)이 불가하지만, default 메소드는 상속(extends와 implements)이 가능하다.
        • 인터페이스들의 추상 메소드는 선언부만 있고 구현부가 없기 때문에 같은 시그니처끼리의 충돌 문제가 없다.
        • 인터페이스 다중 상속 시, 조상 인터페이스들에서 같은 시그니처의 default 메소드가 존재하면, 충돌이 발생하기 때문에, 반드시 구현 클래스나 자식 인터페이스에서 재정의(오버라이딩) 해야한다.
  • 중첩 클래스(내부 클래스)와 중첩 인터페이스(내부 인터페이스)
    • 중첩 클래스란 클래스 내부에 선언한 클래스를 말한다.
    • 중첩 인터페이스란 클래스 내부에 선언한 인터페이스를 말한다.
      • 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서다.
      • 클래스의 멤버로서 선언된 중첩 클래스를 멤버 클래스라 한다.
        • 인스턴스 멤버 클래스
        • class A{ class B{ 생성자 인스턴스 필드 인스턴스 메소드 } }
        • 정적 멤버 클래스
        • class A{ static class B{ 생성자 인스턴스 필드 인스턴스 메소드 정적 필드 정적 메소드 } }
      • 클래스의 생성자 또는 메소드 내부에서 선언된 중첩 클래스를 로컬 클래스라 한다.
        • 로컬 클래스 메소드 내부에서만 사용 가능하므로 따로 접근을 제한 필요가 없기 때문에 접근 제한자 키워드를 사용하지 않는다.
        • 로컬 클래스는 메소드가 실행될 때, 객체를 생성하고 사용해야 한다.
          class A{
              void method(){
                  class B{
                      생성자
                      인스턴스 필드
                      인스턴스 메소드
                  }
              }
          }
    • 익명 객체
      • 익명 객체는 이름이 없는 객체를 말한다.
      • 익명 자식 객체
      • Parent p = new Parent(param,...){ //필드 //메소드 }
      • 익명 구현 객체
      • 인터페이스 interface = new 인터페이스(){ // 추상 메소드의 실체 메소드 선언 // 필드 // 메소드 }
  • 모듈, 패키지, 클래스 패스
    • 패키지는 서로 관련있는 클래스나 인터페이스의 컴파일된 클래스(.class) 파일들을 한 곳에 묶어놓은 것이다.
    • JDK 9부터는 패키지들을 모듈이라는 단위로 묶어, 100개에 가까운 모듈을 제공한다.
    • 모듈은 jdk 설치 디렉터리 밑의 jmod 디렉터리에 .jmod 확장자를 가진 압축 파일 형태로 제공된다.
    • jdk 8 => rt.jar , jdk 9 이후 => modules
    • 클래스 클래스 파일(.class), 패키지는 폴더, 하위 패키지는 하위 폴더
    • 패키지 선언
      • 패키지는 소스 파일의 첫 번째 문장으로 단 한번 선언
      • 같은 소스 파일의 클래스들은 모두 같은 패키지에 속하게 된다.
      • 패키지 선언이 없으면 이름없는 패키지(default package)에 속하게 된다.
    • 클래스 패스(class path)
      • 클래스 파일(*.class)의 위치를 알려주는 경로
      • 운영체제의 환경변수 classpath로 관리하며,경로를 여러 개 등록해줄 수 있고, 경로 간의 구분자는 ';'이다.
      • classpath(환경변수)에 패키지의 루트를 등록해줘야 한다.
    • import 문
      • 클래스를 사용할 때 패키지 이름을 생략할 수 있다.
      • java.lang 패키지의 클래스는 import 하지 않고도 사용할 수 있다.
      • java.lang 패키지 클래스들(String, Object, System, Thread, ...)
      • import 문은 패키지 선언문과 클래스 선언 사이에서 선언해야 한다.
      • import문은 컴파일 시에 처리되므로 실제 프로그램 실행 성능과는 관련없다.
      • import java.util.* 로 해도 실행 성능이 저하되지 않는다.
      • 이름이 같은 클래스가 속한 두 패키지를 import할 때는 정확하게 패키지명과 클래스명을 기입해야 한다.
        • ex ) java.sql.Date와 java.util.Date
      • static import 문
        • static 멤버를 사용할 때 클래스 이름을 생략할 수 있게 한다.
        • Math.random() -> import static java.lang.Math.random; -> random()으로 클래스명 없이 호출 가능
    • 자바 플랫폼의 모듈화
      • 오라클은 Java 9의 도입과 함께 자바 플랫폼 전체를 모듈화하였다.
      • 자바 플랫폼이란 프로그램의 개발 환경과 실행 환경을 함께 지칭하는 것으로, JDK/JRE 형태로 자바 개발자에게 제공된다. 오라클은 자바 API를 모듈화하여, 패키지의 계층 구조로만 되어 있던 클래스들을 수십개의 작은 모듈들로 재구성하였다.
      • 모듈들 중에서 꼭 필요한 기본 모듈이 java.base 모듈이며, java.base.jmod 파일에 들어있다.
      • .jmod 파일은 ZIP 포맷으로 압축된 것으로, JDK의 bin 디렉터리에 있는 jmod 프로그램을 이용하여 압축을 풀 수 있다.
    • 모듈 기반의 자바 실행 환경
      • 자바 실행 환경이란 자바 응용프로그램이 실행되는데 필요한 제반 환경으로서, 응용프로그램이 실행 중에 사용하는 자바 API 클래스와 자바 가상 기계 등으로 이루어진다.
      • Java 8까지의 자바 실행 환경
        • 자바 API의 모든 클래스들은 rt.jar라는 하나의 단일체로 구성
        • 자바 가상 기계는 응용 프로그램 실행 도중, 필요한 클래스 파일을 rt.jar에서 풀어 메모리에 로딩하고 실행하였다. 자바 응용 프로그램이 일부 클래스만 사용하더라도 rt.jar 전체가 설치되는 구조였다.
      • Java 9부터는 rt.jar를 버렸다.
        • 대신 자바 API를 많은 수의 모듈로 분할하여, 자바 응용 프로그램을 컴파일할 때, 실행에 필요한 모듈들만으로 조립하여 구성하도록 하였다.
        • Java 9 이상에서는 modules라는 비공개 파일을 가지고, JVM이 자바 응용 프로그램을 실행할 때, 여기서 필요한 모듈을 끌어내어 실행 환경을 만든다.
    • 자바 모듈화의 목적
      • 자바 모듈화는 여러 목적이 있지만, 자바 컴포넌트들을 필요에 따라 조립하여 사용하기 위함이다.
      • 세밀하 모듈화를 통해, 필요없는 모듈이 로드되지 않게 하여, 컴퓨터 시스템에 불필요한 부담을 줄인다.