본문 바로가기
to Developer/JAVA

Java 기초

by EH헌 2024. 1. 29.

Java 자바 기초에 대해 자주 보기 위해 정리!


접근 제한자

  • public : 접근에 제한이 없음
  • private : 자기 자신 클래스 내에서만 접근 가능
  • default : 동일한 패키지 내에서만 접근 가능
  • protected : 동일한 패키지 내에서만 접근 가능 + 상속을 이용한 접근 가능

String vs Char

Char은 내용물이 1개인 문자로 제한되는 반면에 String은 문자열을 담을 수 있다.
Char의 경우 변수 안에 직접적으로 문자를 가지고 있지만 String은 reference 타입으로 실질적인 문자열이 아니라 주소값을 가지고 있다.
이 때문에 비교 방식에 차이가 있다.
Char의 경우 값이 같다면 ==(동일성) 비교를 사용할 수 있지만, String의 경우 내용이 같더라도 생성되는 주소가 다르기 때문에 == 비교를 사용하면 다른 결과가 나오게 되고 equals를 사용해야 한다.


== 과 equals

  • ==
    • 참조 비교로 두 객체가 같은 메모리 공간을 가리키는지 확인
  • equals
    • 두 객체의 내부 값이 같은지 내용을 비교한다.
    • 기본 타입(Primitive Type)에 대해서는 적용할 수 없다.
    • 객체 비교시 override해서 원하는 방식으로 수정할 수 있다.

데이터 타입

  • Value Type
    • 기본 Primitive 타입으로 int, char 등이 있다.
    • 기본 타입의 크기가 작고 고정적이기 때문에 메모리 Stack 영역 에 저장된다.
    • 정수형 : byte, short, int, long
    • 실수형 : float, double
    • 논리형 : boolean
    • 문자형 : char
  • 참조 타입(Reference Type)
    • 기본형을 제외하고는 모두 참조형이다.
    • String과 박싱 타입인 Integer 등이 있다.
    • 참조 타입은 데이터의 크기가 가변적이고, 동적이므로 Heap 영역 에서 관리된다.
    • 데이터는 Heap 영역에서 관리되지만 메모리의 주소값은 Stack 영역에 담긴다.
    • new 키워드를 이용해 객체를 생성하여 데이터가 생성된 주소를 참조하는 타입
    • String과 배열은 일반적인 참조 타입과 달리 new 없이 생성 가능하지만 참조타입이다.
    • 더이상 참조하는 변수가 없을 때 GC에 의해 삭제된다.

Call By Value와 Call By Reference

  • Call By Value (값에 의한 호출)
    • 함수 호출 시 인자로 전달되는 변수의 값을 복사하여 함수의 인자로 전달 한다.
    • 따라서, 함수 안에서 인자의 값이 변경되어도, 외부의 변수의 값은 변경되지 않는다.
  • Call by Reference (참조에 의한 호출)
    • 함수 호출 시 인자로 전달되는 변수의 레퍼런스를 전달한다.
    • 따라서 함수 안에서 인자의 값이 변경되면, 인자로 전달된 변수의 값도 함께 변경된다.

자바는 새롭게 지역 변수(다른 주소)를 만들어서 값만 복사하고 할당한다. 따라서 자바는 Call By Value에 해당한다.


hashcode

두 객체가 동일한 객체인지 비교할 때 사용하고 heap 영역에 저장된 객체의 메모리 주소를 반환한다.


Wrapper class

프로그램에 따라 기본 타입의 데이터를 객체로 취급해야하는 경우가 있다.
예를 들어, 메서드의 인수로 객체 타입만이 요구된다면, 기본 타입의 데이터를 그대로 사용할 수는 없다.
이때 기본 타입의 데이터를 먼저 객체로 변환 후 작업을 수행해야 한다.
래퍼 클래스(Wrapper class)는 산술 연산을 위해 정의된 클래스가 아니므로, 인스턴스에 저장된 값을 변경할 수 없다.

기본 타입 래퍼 클래스
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

래퍼 클래스는 각각 타입에 해당하는 데이터를 인수로 전달받아, 해당 값을 가지는 객체로 만들어준다.


박싱, 언방식

래퍼 클래스는 산술 연산을 위해 정의된 클래스가 아니므로, 인스턴스에 저장된 값을 변경할 수 없다.
단지, 값을 참조하기 위해 새로운 인스턴스를 생성하고, 생성된 인스턴스의 값만을 참조할 수 있다.
기본 타입을 래퍼클래스의 인스턴스로 변환하는 과정을 박싱, 래퍼클래스의 인스턴스에 저장된 값을 다시 기본 타입으로 꺼내는 과정은 언방식 이라고 한다.

Integer num = new Integer(17); // 박싱
int n = num.intValue();        // 언박싱
System.out.println(n); // 출력 값: 17


Character ch = 'X'; // Character ch = new Character('X'); : 오토박싱
char c = ch;        // char c = ch.charValue();           : 오토언박싱
System.out.println(c); // 출력 값: X

JDK1.5 부터는 위와 같이 자바 컴파일러가 알아서 언방식, 박싱 작업을 해준다.

public class Wrapper03 {
    public static void main(String[] args) {
      Integer num1 = new Integer(10);
      Integer num2 = new Integer(20);
      Integer num3 = new Integer(10);

      System.out.println(num1 < num2);       // true
      System.out.println(num1 == num3);      // false
      System.out.println(num1.equals(num3)); // true
    }
}

래퍼 클래스를 비교할 때는 ==를 사용하면 안되고, equals 메서드를 사용해야 한다.
래퍼 클래스는 객체 이므로 주소값 비교가 아니라 내부 값을 비교하는 equals를 써야한다.


non-static vs static

  • non-static
    • 공간적 특성
      • 객체마다 별도고 존재하고 인스턴스 변수라고 부른다.
    • 시간적 특성
      • 객체와 생명주기가 동일하다.
  • static
    • 공간적 특성
      • 클래스당 하나만 생성된다.
      • 동일한 클래스의 모든 객체들에 의해 공유된다.
    • 시간적 특성
      • 객체가 생기기 전에 이미 생성되어 객체를 생성하지 않아도 사용 가능하다.
      • 객체가 사라져도 사라지지 않는다.
      • 프로그램 종료시에 사라진다.

main이 static인 이유

static 멤버는 프로그램 시작 시(클래스 로딩) 메모리에 로드되어 인스턴스를 생성하지 않아도 호출이 가능하기 때문이다.

  • 실행 과정
    • 코드를 실행하면 컴파일러가 .java를 .class 바이트 코드로 변환
    • 클래스 로더가 .class 파일을 메모리 영역에 로드
    • Runetime Data Area 중 Method Area(=Class area, Static area)라고 불리는 영역에 Class Variable이 저장되는데, static 변수도 여기에 포함
    • JVM은 Method Area에 로드된 main()을 싱행

final vs finally vs finalize

  • final 키워드
    • 변수, 메서드 클래스가 변경 불가능 하도록 만든다.
    • 기본 타입 변수에 적용 시
      • 해당 변수의 값 변경 불가능하다.
    • 참조 변수에 적용 시
      • 참조 변수가 힙 내의 다른 객체를 가리키도록 변경할 수 없다.
    • 메서드에 적용 시
      • 해당 메서드를 오버라이드할 수 없다.(오버로딩은 가능)
    • 클래스에 적용 시
      • 해당 클래스를 상속 받아서 사용할 수 없다.
  • finally 키워드
    • try catch 블록 뒤에서 항상 실행될 코드 블록을 정의하기 위해 사용한다.
  • finalize 메서드
    • 가비지 컬렉터가 더 이상의 참조가 존재하지 않는 객체를 메모리에서 삭제하겠다고 결정하는 순간 호출된다.

try with resources

자바 7 이전에는 try-catch-finally에서는 리로스의 생성을 try 구문에서 리소스의 반납은 finally 구문에서 하다보니 실수의 발생 여지가 있었다.
자바 7 이후에는 try with resources 구문이 나오게 됬는데 try 옆 괄호 안에서 리소스를 생성해주면 따로 반납하지 않아도 리로스를 자동으로 반납해주게 된다.
아래와 같이 작성한 것이 try with resources 구문이고, 만약 try-catch-finally를 사용했다면 finally 구문에서 scanner을 close하는 내용이 있어야 한다.

public class ResourceClose {
    public static void main(String[] args) {
        try (Scanner scanner = new Scanner(new File("input.txt"))) {
            System.out.println(scanner.nextLine());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

제네릭

  • 클래스나 메서드에서 사용할 내부 데이터 타입을 외부에서 지정하는 기법
  • 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지할 수 있다.
  • 불필요한 타입 변환을 제거할 수 있다.

직렬화와 역직렬화

  • 직렬화
    • 자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 자바 시스템에서도 사용할 수 있도록 바이트 형태로 데이터를 변환하는 기술
    • 조건
      • 자바 기본 타입
      • Serializable 인터페이스 상속받은 객체
    • ObjectOutputStream 객체를 이용
  • 역직렬화
    • 바이트로 변환된 데이터를 다시 객체로 변환하는 기술
    • ObjectInputStream 객체를 이용

오버로딩, 오버라이딩

  • 오버로딩
    • 두 메서드가 같은 이름을 갖고 있으나 인자의 수나 자료형이 다른 경우
  • 오버라이딩
    • 하위 클래스에서 상위 클래스의 메서드와 일치하는 함수를 재정의하는 것

추상 클래스와 인터페이스 차이

  • 추상 메서드
    • abstract 키워드와 함께 원형만 선언되고 코드는 작성하지 않은 메서드
  • 추상 클래스
    • 개념 : abstract 키워드로 선언된 클래스
      • 추상 메서드가 최소 한 개 이상을 가진 abstract 클래스
      • 추상 메서드 이외의 다른 것들도 추가 가능
    • 목적
      • 관련성이 높은 클래스 간의 코드를 공유하고 확장하고 싶은 목적
  • 인터페이스
    • 개념 : default와 static 을 제외하고는 추상 메서드와 상수만을 포함하여, interface 키워드를 사용하여 선언
      • 모든 메서드는 추상 메서드로, abstract public이 생략되어 있다.
      • 상수 필드는 public static final이 생략되어 있다.
      • 다중상속이 가능하다.
    • 목적
      • 관련성이 없는 클래스들의 논리적으로 같은 기능을 자신에 맞게 구현을 강제하는데 목적

정리하자면, 구조상 차이로 추상 클래스는 abstract 키워드가 붙고 추상 메서드뿐만 아니라 다른 변수, 메서드도 선언이 가능하고, 인터페이스는 추상 메서드와 상수만 선언 가능하다.(자바 8부터는 default 메서드 선언 가능)
목적의 차이로 추상 클래스는 관련성이 높은 클래스 간의 코드 공유(재사용)와 확장을 목적으로 하고, 인터페이스는 관련성이 없는 클래스들의 같은 기능을 자신에 맞게 구현하는데 목적이 있다.


Error, Exception

목록 Error Exception
패키지 java.lang.error java.lang.exception
발생 시점 런타임에서 발생, 컴파일 시점에서 알 수 없다. Checked Exception은 컴파일 시점에, Unchcked Exception은 런티림 시점에 알 수 있다.
복구 에러는 복구 불가능 try catch 블락을 이용하여 복구 가능
타입 모든 Error는 Unchecked Type checked Type, Unchecked Type으로 분류
예시 OutOfMemory, StackOverFlow 아래서 설명

Checked Exception, Unchecked Exception

구분 Checked Exception Unchecked Exception
상속 RuntimeException 상속 X RuntimeException 상속
확인 시점 컴파일 시점 런타임 시점
처리 여부 반드시 예외처리 명시적으로 하지 않아도 됨
트랜잭션 처리 예외 발생시 롤백 X 예외 발생시 롤백 해야함
종류 IOException,SQLException.. NullPointException,ArrayIndexOutOfBounds...

대부분 Checked Exception보다는 Unchecked Exception 사용을 권장한다.
Checked Exception의 경우 사용하는 모든 곳에 throws를 남겨야하는데 이 문제는 의존성 문제를 야기한다.
예를 들어 가장 하위에서 SQLException(Checked Exception)를 던진다고 해보자.
그럼 상위 서비스, 컨트롤러도 SQLException을 처리하기 위해서 throws SQLException을 붙이게 된다.
SQLException은 JDBC 기술이므로 service, controller는 JDBC에 의존하게 된다.
결국 JDBC 기술을 다른 기술로 교체하게 되면 연결된 모든 것들을 전부 교체해야하는 문제가 생긴다.


자료 참고
[참고 1]

'to Developer > JAVA' 카테고리의 다른 글

JVM  (0) 2024.01.28
객체 지향 프로그래밍  (0) 2024.01.27
Java의 특징  (0) 2024.01.27