Java/whiteship-java-study

2주차 - 자바 데이터 타입, 변수 그리고 배열

ysk(0soo) 2022. 7. 31. 17:04

백기선 님의 유튜브 온라인 자바 스터디를 정리한 글입니다

2주차

WEEK 02 :: 자바 데이터 타입, 변수 그리고 배열

  • 프리미티브 타입 종류와 값의 범위 그리고 기본 값
  • 프리미티브 타입과 레퍼런스 타입
  • 리터럴
  • 변수 선언 및 초기화하는 방법
  • 변수의 스코프와 라이프타임
  • 타입 변환, 캐스팅 그리고 타입 프로모션
  • 1차 및 2차 배열 선언하기
  • 타입 추론, var

프리미티브 타입 종류와 값의 범위 그리고 기본 값

  • Primitive Type : 원시 타입, 기본형 타입. 변수의 주소 값에 값이 그대로 저장되는 데이터 타입
    • 값이 할당되면서 실제 값을 JVM의 Runtime Data Area 영역 중 Stack 영역에 값이 저장
    • 기본 값이 있기 때문에 Null이 존재하지 않는다. Null을 넣고 싶다면 래퍼(Wrapper) 클래스를 활용.
    • 값이 범위를 벗어나면 컴파일 에러 발생
    • 값 저장.
구분 타입 기본값 값의 표현 범위 할당되는 메모리 크기
논리형 boolean false true, false 1byte
정수형 byte 0 -128 ~ 127 1byte
  short 0 -32,768 ~ 32,767 2byte
  int 0 -2,147,483,648 ~ 2,147,483,647 4byte
  long 0L - 9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 (-2^63 ~ 2^63-1) 8byte
실수형 float 0.0f (3.4 X 10^38) ~ (3.4 X 10^38) 의 근삿값 4byte
  double 0.0 (1.7 X 10 ^ -308) ~ (1.7 X 10^308)의 근삿값 8byte
문자형 char '\u0000' 0 ~ 65,535 2byte
  • 1bit = 0 또는 1, 2진수 1자리
    • 비트가 1자릿수 늘어나면 표현할 수 있는 표현 범위는 2배가 됨
      • 2비트 = 4개, 3비트 = 8개, 4비트 = 16개.
      • 즉 2의 제곱승(2^비트)으로 표현 가능
  • 1 byte = 8 bit
  • 최상위 비트는 양수와 음수를 표현. - 부호비트
    • 값이 1이면 음수, 0이면 양수
    • X 0 0 0 0 0 0 0
    • 숫자 0은 값, X는 부호비트
  • 실수는 부호, 가수, 지수로 구성. 부동 소수점 방식 사용
    • 모둔 가수를 0보다 크거나 같고, 1보다 작은 값 범위의 값으로 만들고, 10의 제곱수로 표현
    • float 타입 : 부호(1비트) + 지수(8비트) + 가수(23비트) = 32비트 = 4바이트
    • double 타입 : 부호(1비트) + 지수(11비트) + 가수(52비트) = 64비트 = 8바이트

프리미티브 타입과 레퍼런스 타입

  • Reference Type : 참조형 타입. Primitive Type을 제외한 타입들을 이야기 한다.
    • 실제 값이 저장되어 있는 곳의 주솟값 을 저장하고 힙(Heap) 메모리에 저장된다.
    • 실제 객체는 힙 영역에 저장되며 참조 타입 변수(인스턴스)는 스택 영역에 있는 실제 객체들의 주소를 저장하여, 객체를 사용할 때 마다 참조변수(인스턴스)에 젖아된 객체의 주소를 불러와 사용한다.
      • 다만, 참조 변수의 값인 주솟값이 가리키는 실제 값은 가비지 컬렉션 힙 영역에 객체가 생성된다.
      • Primitive Type의 경우 실제 값이 아닌 주소값이 복사되기 때문.
    • 레퍼런스 타입은 미리 정의되어 있지 않고 종류는 무한하다. - 개발자가 작성해서 정의하기 때문
    • 모든 레퍼런스 타입은 java.lang.Object 유형의 하위 클래스이다.
    • Null을 저장할 수 있다.
    • 주소값을 저장.
  • Refrence Type 종류
    • class
    • interface
    • array
    • enum
    • string
  • 크기 : 4byte (객체의 주솟값)
  • 주솟값을 참조한다?
public class Car {
  private String name;
  private int number;

  public static void main(String[] args) {
    String carName = "kia";
    int carNumber = 4848;
         Car kia = new Car(carName, carNumber);
  }
}
  • carName 와 carNumber 변수는 런타임 스택 영역에 생성된다.
  • Car를 인스턴스화 (new) 시키면 Heap 영역에 name은 "kia", number는 "4848" 인 객체가 생성되고, kia 변수는 Heap 영역에 저장된 객체 의 주소가 저장된다.
  • 주소에 의한 복사(call by reference)
    1. 얕은 복사 : 주소값을 복사하여 동일한 객체를 참조.
      • 값을 수정하면 복사된 원본 객체도 변함.
    2. 깊은 복사 : primitive type에서의 값에 의한 복사처럼 완전히 똑같은 새로운 객체를 만들어 복사.
      • 값을 수정해도 복사된 원본 객체는 변하지 않음.
  • 정적 메모리 스택(Stack) 영역
    • 스택 영역에는 Primitive Type 변수가 할당되고 변수의 실제 들이 저장된다.
    • Reference Type 의 변수들은 이 스택 영역에서 힙 영역에 생성된 객체들의 **주소 값을 저장**한다
    • 객체 안의 메소드의 작업이 종료되면 할당되었던 메모리 공간은 반환되어 비워진다.
  • 동적 메모리 힙(Heap) 영역
  • 힙 영역에는 객체와 배열이 생성된다.
  • 그리고 참조타입(배열, 클래스, 인터페이스 등)들이 이 객체들의 주소스택 영역에 저장한다.
  • 프로그램 실행시 메모리에 동적으로 할당된다.
  • 참조하는 변수가 없으면 가비지 컬렉터가 회수해서 제거한다.

리터럴

  • 실제로 변수나 상수에 저장되는 값 그 자체를 의미
  • Primitive Type 과 String이 있다.
    • 정수 리터럴, 실수 리터럴, 문자 리터럴, 논리 리터럴, 문자열 리터럴
int age = 28;
double average = 90.5;
boolean isHungry = true;
char firstCharacter = 'A';
String name = "ys";
  • 다른 리터럴들은 Primitive Type 의 값을 표현하기 위해 존재.
  • Primitive Type은 값을 Heap 메모리 영역 안에 있는 Constant Pool 이라는 메모리 영역에 할당
    • 똑같은 값을 또 호출 했을 때 전에 할당했던 메모리 주소를 넘겨주게 됩니다.
    • a == b 가 true인 이유는 a 와 b가 똑같은 메모리 주소를 가르키고 있기 때문입니다.
      • ** ==은 메모리 주소를 비교.**
  • 문자열(String)은 Reference Type 이지만 literal 을 지원
  • literal 방식으로 String 에 값을 주면 Heap 영역에서 String constant pool 이라는 특수한 영역에 값이 저장된다.
  • 동일한 값을 쓰는 경우에 다른 일반적인 레퍼런스타입 처럼 Heap 에 또 올라가지 않고, String constant pool 에 존재하는 값을 참조하는 방식으로 작동

변수 선언 및 초기화하는 방법

  • 선언 : 타입 변수명;
  • 초기화 : Primitive 타입은 대입 연산자로 대입, Reference 타입은 대입 연산자로 대입하거나 new로 선언
int brotherAge; // 선언
brotherAge = 30; // 초기화. 선언한 다음 초기화
int age = 28; // 대입연산자로 대입. 선언과 동시에 초기화

Car kia;
kia = new Car();
Car audi = new Car();
  • 변수를 선언하고 나면 해당 공간에는 쓰레기 값이 들어가 있게 된다.
  • 초기화를 해주지 않으면 컴파일 에러가 난다.
  • ReferenceType의 경우 NullPointerException이 발생.

변수의 스코프와 라이프타임

  • 스코프(Scope) : 범위.
    • 변수의 스코프란 변수의 해당 변수를 사용할 수 있는 범위이다.
  • 라이프타임(Lifetime) : 생존 기간
    • 변수가 메모리에서 살아있는 기간을 뜻한다.
  • 변수의 스코프는 자바에서는 일반적으로 블록 스코프를 사용한다 { } (블록 : {}, 중괄호)
  •  

변수 유형

  • Instance Variables(인스턴스 변수)
    • 클래스 내부와 메소드 본문 외부에 선언된 변수
    • 스코프 : 정적 메서드(static method)를 제외한 클래스 전체
    • 라이프타임 : 클래스를 인스턴스화 한 객체가 메모리에 남아있을 때 까지
    • 생성 시기 : 인스턴스가 생성 되었을 때
  • Local Variables(지역 변수)
    • 인스턴스 및 클래스 변수가 아닌 변수. 메소드 내에 사용할 수 있다.
    • 로컬 변수는 정적(static)으로 선언할 수 없다.
    • 스코프 : 변수가 선언된 {}(블록) 내부
    • 라이프타임 : 선언 된 블록을 떠날 때 까지
    • 생성 시기 : 변수 선언문이 수행 되었을 때
  • Class Variables (클래스 변수)
    • 클래스 내부, 모든 블록 외부에서 선언되고 정적(static)으로 표시된 변수
    • 스코프 : 클래스 전체
    • 라이프타임 : 프로그램 종료시 까지
    • 생성 시기 : 클래스가 메모리에 올라갈 때
  • 어떠한 method나 block안에서 선언되지 않은 변수
    • scope - static method를 제외한 클래스 전체
    • lifetime - 클래스를 인스턴스화한 객체가 메모리에서 사라질 때 까지
public class SimpleCalculator {
  private int x; // 인스턴스 변수
  private int y; // 인스턴스 변수

  private static final double PI = 3.14; // 클래스 변수

  public int sum(int x, int y) { // x, y는 지역 변수 
    return x + y;
  }

  public void printSum(int x, int y) { // x, y는 지역 변수 
    int sum = x + y; // 지역 변수

    System.out.println("print sum. result : " + sum);
  }
  ...

}

타입 변환, 캐스팅 그리고 타입 프로모션

  • 타입 변환 : 자료형을 다른 데이터 타입으로 변환하는것
  • 타입 변환에는 2가지 종류가 있다. 타입 프로모션(Type promotion) 과 타입 캐스팅(Type casting)
    • 타입 프로모션과 타입 캐스팅을 구분하기 위해서는 메모리 크기가 아닌 데이터 표현 범위를 생각해야 한다.
    • 이렇게 원본 데이터가 담긴 데이터 타입의 표현 범위를 변환 할 데이터 타입의 표현 범위가 모두 수용하지 못할 경우 데이터 손실이 발생할 수 있는데, 이것을 타입 캐스팅 이라고 한다.
  • class 타입 객체들도 형변환이 가능한데, 타입캐스팅과 프로모션 캐스팅을 정리한 다음에 정리해놓았습니다.

타입 캐스팅(Type casting)

  • 크기가 더 큰 자료형을 더 작은 자료형으로 변환하는것 -> 변환할 자료형 변수 = (변환할 자료형)변환할 변수;
    • 자신의 표현 범위를 모두 포함하지 못한 데이터 티입으로의 변환
    • 원본 데이터가 담긴 데이터 타입의 표현 범위를, 변환 할 데이터 타입의 표현 범위가 모두 수용하지 못할 경우 데이터 손실이 발생할 수 있다
    • Primitive Type들은 boolean을 제외하고는 타입 캐스팅이 모두 가능하며, 데이터 손실이 있을 수는 있지만 캐스팅이 실패하지는 않는다.
int intValue = 10;     
byte byteValue = (byte)intValue;
System.out.println(byteValue); //  -> 10
(byte 는 -256 ~ 255 까지 표현할 수 있고, 원본 데이터인 10은 표현 범위를 벗어나지 않으므로 타입캐스팅을 했을 때 데이터 변형이나 
손실은 오지 않는다) 
// ----------------------
int intValue = 10000;     
byte byteValue = (byte)intVAlue;
System.out.println(byteValue); //  -> 16
(byte의 표현범위를 벗어나는 값을 강제로 타입캐스팅해 수용하지 못하므로 10000에서 16으로 데이터에 변형이 생겼다) 

타입 프로모션(Type promotion)

  • 기존 타입에서 더 큰 타입으로 변경을 할 때 프로모션 변경이 발생.
    • 자신의 표현 범위를 모두 포함한 데이터 타입으로의 변환
    • 원본 데이터가 담긴 데이터 타입의 표현 범위를 모두 수용할 수 있다.
  • 타입 프로모션의 경우 어떤 데이터 타입으로 변환해야하는지 명시하지 않아도 됩니다. (변환할 자료형) 이 필요 없음
    • 자동으로 변환.
byte byteValue = 10;
int intValue = byteValue;
System.out.println(intValue); // -> 10

Class 타입 형변환

  • 클래스 타입 변환은 상속 관계에 있는 클래스 사이에서 가능합니다.
    • 자바에서는 객체지향의 특징인 다형성을 통해서.
  • 상속 관계가 아니면 클래스 타입에서는 타입 캐스팅이 불가능합니다.
  • 업캐스팅과 다운 캐스팅이 있습니다.
public class Parent {

}

public class Child extends Parent {

}

업캐스팅 (Upcasting)

  • 자식 타입 클래스의 객체가 부모 클래스 타입으로 형 변환 하는 것.
Child child = new Child();
Parent parent = child;
  • 자식 타입은 부모 타입으로 자동 타입 변환이 가능하다. Parent parent = child;
  • 자식은 부모의 특징과 기능을 상속받기 때문에 부모와 동일하게 취급될 수 있습니.
    • 자식의 자식인 손자여도 (상속 계층에서의 상위타입) 자동 타입 변환이 가능합니다.
  • 부모 클래스 타입으로 형 변환된 자식 객체는, 부모 클래스에 선언된 필드와 메서드만 접근, 사용 가능합니다
    • 비록 변수는 자식 객체를 참조하지만, 변수로 접근 가능한 멤버는 부모 클래스 멤버로 한정
    • 예외로, A라는 메서드가 자식 클래스에서 오버라이딩 되었다면 자식 클래스의 오버라이딩된 A메서드가 대신 호출됩니다.

다운캐스팅(Downcasting)

  • 부모타입 클래스의 객체가 자식 클래스 타입으로 형 변환 하는 것
Parent parent = new Parent();
Child child = (Child)parent;
  • 부모 타입은 자식 타입으로 자동으로 타입 변환이 불가능 하기에 명시적으로 자식 클래스 자료형을 명시해줘야합니다.
  • 형 변환하게되면 자식 클래스에서 선언된 메서드와 필드를 사용할 수 있습니다.
  • 왜 자식 -> 부모는 자동 형변환이 가능하고, 부모 -> 자식은 명시적 타입 캐스팅이 필요할까?
    • 자식클래스는 부모클래스의 필드나 메소드를 물려받음으로 자식클래스타입의 객체를 부모클래스타입으로 바꾼다고 해서 데이터의 손실이나 변형이 일어나진 않기 때문이다.

1차 및 2차 배열 선언하기

  • 배열 (Array) : 동일한 타입의 데이터를 연속된 공간에 저장하기 위한 레퍼런스 타입의 자료구조
    • 동일한 데이터 타입을 하나의 배열로 관리할 수 있고, 인덱스는 1이아닌 0번부터 시작한다.
  • 배열은 다음과 같이 선언할 수 있다.
    • 자료형[] 변수명 = new 자료형[배열 크기]
int[] array = new int[10];
  • 배열 변수는 JVM 런타임 스택 영역에 생성되고, 배열의 값들은 가비지 컬렉션 힙 영역에 객체가 생성된다.
    • 이 힙 영역의 주소 값이 런타임 스택 영역에 생성된 배열 변수에 값으로 할당된다.
  • 배열은 한번 생성하면 줄이거나 늘릴 수 없고, 자바에서는 같은 타입들만이 배열의 원소로 사용된다.

1차원배열

  • 선언 : 자료형[] 변수명 = new 자료형[배열 크기]
int[] array = new int[10]; // 선언
int[] otherArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 선언과 할당 
  • 한줄로 이어져 있다고 생각하면 된다.

2차원 배열

  • 선언 : 자료형[][] 변수명 = new 자료형[열][행]
  • int[][] array = new int[3][5]
    • 정수를 5개씩 저장할 수 있는 3개의 배열
  • 행렬 또는 큐브 형태라고 생각하면 된다.

타입 추론, var

  • 타입 추론이란, 데이터타입(자료형)을 명시하지 않아도 컴파일러가 데이터 타입이 무엇인지 추론하는 것을 의미.
    • 명시적이게 선언하지 않고 var로 선언하는것
    • 자바10부터 지원
public static void main(String[] args) {
  var str = "hi";
  var intValue = "1";
}
  • 타입 추론 (var)을 사용할 경우 제약 사항이 있다.
    1. 로컬 변수(local vairables) 이여야 한다.
    2. 선언과 동시에 값이 초기화 되어야 한다.
    3. null 값이 들어갈 수 없다.
    4. Lambda Expression에서는 명시적으로 타입을 지정해 줘야 한다
    5. 배열을 선언할 때 var 대신 타입을 명시 해줘야 한다.
  • var는 키워드가 아니고 예약어가 아니다.
  • 참조 및 출처

https://www.learningjournal.guru/article/programming-in-java/scope-and-lifetime-of-a-variable/

https://gbsb.tistory.com/6

https://www.geeksforgeeks.org/types-references-java/

https://www.javatpoint.com/reference-data-types-in-java

https://yoo11052.tistory.com/50

https://league-cat.tistory.com/411