상세 컨텐츠

본문 제목

[JAVA] 자바 스터디 2주차 - 자바 데이터 타입, 변수 그리고 배열

개발 공부 (etc)/JAVA

by letprogramming 2021. 1. 27. 04:40

본문

반응형

목표


자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법을 익힙니다.

 

학습할 것


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

자바 프리미티브 타입(Primitive Type) - 기본 자료형


- 실제값을 저장 -> 스택 메모리에 저장

- 기본값 존재 -> NULL (X)

 

 

 타입

 할당되는 메모리 크기

 기본값

 데이터의 표현 범위

 논리형

 boolean

 1 byte

 false

 true, false

 정수형

 byte

 1 byte

 0

 -128 ~ 127

 short

 2 byte

 0

 -32,768 ~ 32,767

 int(기본)

 4 byte 

 0

 -2,147,483,648 ~ 2,147,483,647

 long

 8 byte

 0L

 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807

 실수형

 float

 4 byte

 0.0F

 (3.4 X 10-38) ~ (3.4 X 1038) 의 근사값

 double(기본)

 8 byte

 0.0  (1.7 X 10-308) ~ (1.7 X 10308) 의 근사값

 문자형

 char

 2 byte (유니코드)

 '\u0000' 

 0 ~ 65,535

 

참조형 타입 (Reference Type)


- NULL 존재 (빈 객체를 의미)

- 값의 주소값을 저장 -> 힙 메모리에 저장

 타입

예시

 기본값

할당되는 메모리 크기 

 배열(Array)

 int[] arr = new int[5];

 Null

 4 byte (객체의 주소값)

 열거(Enumeration)

 

 Null

 클래스(Class)

 String str = "test";

 Student sujin = new Student();

 Null

 인터페이스(Interface) 

 

 Null

 

변수 선언 및 초기화, 리터럴 (Literal)


변수란 데이터를 담는 그릇과 같은 개념이다.

데이터의 종류에 따라 이 그릇의 모양이 달라지는데 위에서 언급한 타입(Type)이 이 데이터의 종류이다.

자바에서 이 변수를 선언하고 초기화하는 방법은 다음과 같다.

EX)

int a;

=> 자료형 변수이름;

=> 변수를 선언할 때는 데이터 자료형을 먼저 작성하고 뒤에 임의의 변수 이름을 붙여 선언한다.

=> 위의 예는 정수형a라는 이름의 변수가 선언되었다.

 

선언이라는 개념은 내가 이러한 변수를 사용할 것이라고 미리 작성하는 것이다.

컴퓨터의 입장에서는 a에 1을 대입한다고 하면 이 a가 무엇인지 선언하지 않고 알 수가 없다.

 

초기화는 변수에 초기값을 넣는 행위를 말한다.

예를 들어

int a;

이렇게 선언하고 나서 a라는 변수에 무슨 값이 있는지 출력하려고 하면 이상한 값이 출력되고

이 변수를 연산에 사용하면 에러가 뜨게 된다.

 

그 이유는 초기화를 안했기 때문이다.

변수를 그릇이라고 비유하고 값을 음식이라고 생각하면

아직 조리되지 않고 그릇에 담겨있는 재료들을 그냥 먹으라고 명령을 내리는 것과 비슷하다고 할 수 있다.

 

초기화를 하는 방법은 간단하다.

변수를 선언함과 동시에 할 수 있고, 변수를 선언한 이후에 할 수 있다.

int a = 5;

=> 정수형 변수 a를 선언하면서 5라는 정수값(초기값)을 바로 대입했다.

=> 이제 변수 a에는 5라는 값이 실제로 저장되어있다.

 

int a;

a = 5;

=> 정수형 변수 a를 선언하고 난 이후에 다음줄에서 5로 초기화하는 코드이다.

=> 변수를 새롭게 선언하는 것이 아니라 이미 선언된 변수에 값을 저장하는 행위를 대입이라 하는데

이 때는 자료형을 쓰지 않고 '변수 이름 = 값' 으로 작성한다.

 

리터럴(Literal) or 즉시값(Immediate value)

리터럴은 변수에 어떤 값을 넣을 때 사용되는 값을 의미한다.

위의 예제에서는 5가 리터럴이라고 할 수 있다.

 

자바에서 float형 리터럴의 끝에는 F를 붙이고 longL, doubleD를 붙인다.

boolean 타입은 true와 false를 리터럴로 갖기 때문에 다른 값이 들어올 수 없다.

 

변수의 스코프(Scope)


변수의 스코프(Scope)란 변수가 유효한 범위, 즉 해당 변수를 접근, 사용할 수 있는 범위를 의미한다.

간단하게 말하면 자바에서는 중괄호{} 안에서 선언한 변수는 그 중괄호가 끝날 때까지 사용할 수 있다.

 

변수의 종류는 이 스코프에 따라

1. 지역 변수 (Local Variables)

2. 멤버 변수 (Member Variables), 인스턴스 변수 (Instance Variables)

3. 정적 변수 (Static Variables), 클래스 변수 (Class Variables)

로 나눌 수 있다.

 

예를 들어

class A
{
	int B = 1;
	int C = 1;
	static int D = 10;
    
	public void test(){
		int B = 10
		System.out.println(B);
	}
}

위와 같은 클래스 A가 존재한다.

클래스 A에서 정수형 변수 B와 C는 멤버 변수, 인스턴스 변수,

같은 이름이지만 test라는 함수에 선언되어 있는 B는 지역 변수이다.

D는 정적 변수, 클래스 변수이다.

 

변수들의 차이점을 알아 보면

먼저 지역 변수는 클래스안의 메소드(함수)나 블록 내부에서 선언한 변수이다.

위의 예에서는 test라는 메소드에서 B를 선언했다.

 

이 지역 변수는 해당 블록이 끝나는 지점까지 사용이 가능하다.

만약 for문 내에서 반복을 위해 i라는 정수형 변수를 선언했다면

이 i는 해당 for문 내에서만 사용이 가능하다.

 

for(int i = 0; i < n; i++){
}

 

이런 식이라면 여기서 i는 for문 내에서 선언된 지역 변수로 for문 내에서만 사용이 가능하다.

 

 

멤버 변수(인스턴스 변수)는 클래스가 가지고 있는 변수, 데이터이다.

먼저 클래스(Class)란 자바의 가장 기본이 되는 자료 구조이다. 나중에 공부하기 때문에 자세히 들여다보지는 않지만,

클래스는 데이터와 메소드를 가질 수 있다.

 

클래스(class) = 데이터(Data) + 메소드(Method)

 

이 때, 가지는 데이터가 멤버 변수라고 생각하면 된다.

위의 예에서는 B와 C가 멤버 변수이다.

이 멤버 변수는 클래스내에서는 당연히 사용이 가능하다.

 

그러나 클래스 외부에서는 사용이 가능할 수도 있고 불가능할 수도 있다. 이는 코드를 작성한 사람이 정해줄 수 있다.

private과 pulbic과 같은 접근자를 이용해 직접적으로 프로그래머가 변수의 사용 범위를 지정해주는 것이다.

이 접근자는 멤버 변수에 사용하며, 변수의 자료형 앞에 붙인다.

private int a;

=> private 접근자를 가지는 정수형의 a라는 변수 선언

 

접근자의 종류

private : 클래스 내에서만 참조 가능

protected : 상속 관계에서 참조 가능

default : 같은 패키지 내에서 참조 가능

public : 모든 곳에서 참조 가능

 

접근자를 어떻게 선언하느냐에 따라서 멤버 변수의 스코프는 변경된다고 할 수 있다.

 

정적 변수(클래스 변수)란  static 키워드를 붙여서 선언한다.

정적 변수는 프로그램이 메모리에 로드되는 순간부터 끝날 때까지 존재한다.

즉, 이 변수를 포함하고 있는 클래스를 생성, 객체화 시키지 않아도 접근할 수 있고 사용할 수 있다는 것이다.

정적 변수는 변하지 않는 주소값을 가지고 있기 때문에 해당 변수의 값을 공유할 수 있다.

 

위의 예에서 다른 클래스에서 B의 값을 변경하려 한다면 변경하는 것은 불가능하다.

그러나 정적 변수인 D의 값을 변경한 후에 출력을 해보면 D의 실제 값이 변경되었음을 알 수 있다.

정적 변수로 선언된 변수는 어디에서 접근해도 항상 같은 메모리 주소이기 때문에

쉽게 접근할 수 있고 변경도 용이하다. 또한 변경된 값이 다른 곳에서도 동기화된다.

 

유사한 변수를 매번 클래스마다 선언하는 것보다 정적 변수를 사용하는 것이 메모리를 효율적으로 사용한다고 할 수 있다.

 

그러나 정적 변수를 빈번하게 사용하면 오히려 시스템에 치명적인 오류를 야기할 수 있다.

접근이 쉽다는 것은 그만큼 어디서든 예상치 못한 값이 저장될 수 있다는 것이다.


멤버 변수와 정적 변수의 차이가 헷갈릴 수 있다.

범위를 가르자면 멤버 변수가 더 큰 범위이고 정적 변수를 포함한다고 할 수 있다.

 

멤버 변수 > 정적 변수

 

메소드가 아닌 클래스내에서 선언한 변수는 멤버 변수이다.

이 변수들 중에서 클래스를 인스턴스로 생성하지 않아도 참조하고, 변경하고 싶은 경우에

static 키워드를 붙여서 선언하면 언제든 해당 변수를 사용할 수 있다.

 

정적 변수가 아닌 멤버 변수는 반드시 클래스를 생성한 이후에 사용할 수 있다.

 

마지막으로 예를 들어

public static int a = 3;

이라는 변수가 있다면,

이 a 변수는 int 타입으로 정수형 자료를 담을 수 있고,

static 으로 클래스를 생성하지 않아도 접근이 가능하고 항상 그 값이 동일하게 공유되며, 

public으로 어디서든지 이 변수에 접근할 수 있다.

 

자바에서 public static으로 선언한 변수는 어디서든지 접근이 가능하기 때문에,

전역 변수(Global Variable)라고 할 수 있다.

 

변수 라이프타임(Lifetime) - 생명주기


우리는 변수를 만드는 법에 대해서 배웠다.

변수를 선언하고 초기화할 수 있고, 어디서 접근이 가능한지 공부했다.

 

위에서 정적 변수에 대해 이야기 할 때, 정적 변수가 아닌 멤버 변수들은 반드시 객체를 통해서 사용할 수 있다고 했다.

왜 생성하지 않으면 사용하지 못하는지는 변수의 생명주기와 관련이 있다.

 

생명주기란 말 그대로 생명의 일대기를 그린 것이다.

인간에 비유하면 태어나고 자라서 죽는 과정이 어떤지를 나타내는 것이라고 할 수 있다.

변수의 생명주기는 변수가 만들어져서 사용되고 어떻게 사라지는지를 말한다.

 

프리미티브 변수와 레퍼런스 변수는 저장되는 메모리의 영역이 달랐다.

프리미티브는 스택 영역에 레퍼런스는  영역에 저장되었다.

이 말은 우리가 변수를 선언하고 초기화하면 스택이나 힙 메모리에 이 변수가 저장된다는 의미이다.

 

변수가 메모리에 로드되어 있을 때 변수는 존재하고 우리는 변수를 사용할 수 있다.

변수는 항상 메모리에 로드되어 있는 것이 아니다.

프로그램이 종료하면 메모리에서 해제되면서 사라진다. 무조건 프로그램이 종료해야 하는 것은 아니다.

변수의 종류에 따라 메모리에 로드되는 시점과 해제되는 시점, 태어나고 사라지는 시점이 다르다.

 

지역 변수

먼저 지역 변수는 메소드나 메소드 내부의 블록에서 선언된 변수이다.

지역 변수는 그 지역 변수를 가지고 있는 메소드가 호출되어야 비로소 메모리로 올라갈 수 있다.

해당 메소드의 모든 것을 수행하고 종료되면 이 지역 변수가 가지고 있던 메모리도 해제한다.

지역 변수는

메소드 호출 -> 변수 메모리 로드 -> 메소드 수행 -> 메소드 종료 -> 변수 메모리 해제

의 순서로 생명 주기를 갖는다고 할 수 있다.

 

멤버 변수(인스턴스 변수)

멤버 변수는 클래스 내부에서 선언된 변수이다.

멤버 변수는 클래스에 종속되기 때문에 클래스가 존재해야 비로소 존재할 수 있다.

클래스가 존재한다는 것은 클래스를 인스턴스화 (객체화)했다는 것이다.

 

객체의 개념은 나중에 공부하므로 간단하게 말하자면

멤버 변수의 생성 시기는 이 멤버 변수를 가지고 있는 클래스가 메모리에 로드가 되면

그 때, 이 멤버 변수도 같이 메모리에 로드가 된다.

 

클래스 생성 -> 멤버 변수 메모리 로드 -> 클래스 객체 해제 -> 멤버 변수 메모리 해제

의 과정으로 생명주기를 갖는다.

 

정적 변수(클래스 변수)

마지막으로 정적 변수는 멤버 변수처럼 클래스 내부에서 선언되지만

static이라는 키워드를 추가로 갖는 변수이다.

이 정적 변수는 멤버 변수와 다르게 자바 프로그램이 시작할 때 메모리에 로드된다.

또한 프로그램이 종료가 되야 정적 변수가 가지고 있는 메모리를 해제한다.

 

프로그램 시작 -> 정적 변수 메모리 로드 -> 프로그램 수행 -> 프로그램 종료 -> 정적 변수 메모리 해제

이러한 생명주기를 가지고 있기 때문에 위에서 말했던 정적 변수의 특성들이 가능했던 것이다.

 

타입 변환(Casting)과 타입 프로모션(Promotion)


캐스팅과 프로모션에 대해 공부하기 전에 형 변환에 대해 먼저 알아본다.

형 변환이란 말 그대로 자료형을 바꾸는 행위이다. 자료형을 바꾸는 일이 발생하는 이유는 자료형마다 크기가 다르기 때문이다.

 

위에서 자료형의 종류에 대해 공부할 때 할당되는 메모리의 양과 이에 따른 데이터 표현 범위에 대해서도 공부했다.

예를 들어

int a와 double b가 있다.

int a는 메모리에서 4byte를 가지고 있을 것이고, double b는 8byte를 할당 받았을 것이다.

그런데 만약 연산을 하다가 a의 값을 b에 대입해야하는 경우가 발생했다.

 

이와 같은 경우, 자료형이 다른 변수들 간의 연산을 위해서는 한 쪽으로 자료형을 맞추어 줘야 한다.

둘 다 int로 바꾸거나, double로 바꾸는 것이다. 이렇게 하는 행위를 형 변환이라고 한다.

 

이제 프로모션을 예시와 함께 알아본다.

아래와 같이 형 변환을 해야하는 경우가 발생했다.

 

int a = 3;

double b = 3.14;

b = a;

int a = 3;
double b = 3.14;
b = a;

를 수행해야 하는 것이다.

 

이처럼 크기가 큰 자료형에 상대적으로 작은 자료형을 대입할 때, 작은 자료형이 큰 자료형으로 자동을 변환되는 것을 프로모션(자동 형 변환)이라고 한다.

 

위의 코드를 실행하면 별도의 작업 없이 자동으로 a를 double 자료형으로 변환해서 대입을 한다.

대입한 이후에 b의 값을 출력하면 3.0이 b에 저장되어 있는 것을 확인할 수 있다.

 

캐스팅(명시적 형 변환)은 위의 프로모션과 반대의 경우이다.

크기가 더 큰 자료형을 작은 자료형에 대입하는 경우에 발생한다.

int a = 3;
float b = 3.14;
a = b;

위의 예시같은 작업을 수행해야 하는 것이다.

 

캐스팅의 경우는 컴퓨터가 알아서 해주지 않는다. 개발자가 직접 알려주어야 한다.

위에서 형 변환을 할 때는 자료형을 한 쪽으로 맞추어 줘야한다고 했다.

 

형 변환을 어디에 맞출지를 개발자가 코드에 명시하는 것이다.
명시하는 방법은 자료형을 바꾸고 싶은 변수의 앞에 소괄호를 쓰고 그 안에 원하는 자료형을 명시하는 것이다.

위의 코드에서

a = b;

이 부분을

a = (int)b;

이렇게 변경하면 float 자료형인 b 변수를 int형으로 변환한 이후에 a에 대입한다.

이후에 a를 출력하면 b의 소수점 아래 정보가 사라지고 3만 a에 저장된 것을 확인할 수 있다.

 

1차, 2차 배열 선언


자바에서 배열을 선언하는 방법을 공부한다.

자바의 배열은 특정한 자료형을 가지고 있는 변수들을 가지고 있는 하나의 자료 구조라고 할 수 있다.

예를 들어,

int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;

이렇게 5개의 int형 변수가 존재할 때, 배열이 없다면 위와 같이 변수 하나 하나를 직접 선언해주어야 하며

변수에 대한 반복적인 접근이나 연산에 대해 처리하기가 굉장히 어렵고 관리가 힘들다.

 

배열을 이용한다면 위의 5개의 변수를 하나의 변수에 담을 수 있다.

arr이라는 배열이 있다면 arr = [1, 2, 3, 4, 5] 로 나타낼 수 있다.

배열은 하나 하나의 값에 인덱스를 통해 직접 접근이 가능하다.

 

자바에서는 배열을 선언할 때

int[] arr; 또는 int arr[]; 로 선언한다.

하나의 배열에는 같은 자료형의 데이터들을 담을 수 있으므로 자료형을 먼저 정해주어야 한다.

 

초기화

변수와 마찬가지로 배열도 선언과 동시에 초기화하거나 선언 이후에 초기화 할 수 있다.

int arr[] = new int[5];

=> 선언과 동시에

int arr[];
arr = new int[5];

=> 선언 이후에

 

여기서 new 키워드는 앞에서 말했었던 인스턴스화 (객체화)와 관련된 개념이다.

나중에 객체에 대해 따로 공부를 하기때문에 간단하게 설명하면 new는 객체를 생성하는 키워드이다.

 

즉 new int[5];의 의미는 5의 크기를 가지고 있는 배열을 생성하는 것이다.

자바에서는 배열도 객체로 보기때문에 new 키워드를 써서 생성을 해주어야한다.

 

또한 이렇게 배열을 생성해서 초기화하면 int형 배열의 경우 배열의 값들은 0으로 초기화된다.

=> [0, 0, 0, 0, 0] 이러한 배열이 탄생하는 것이다.

 

배열의 초기화 방법은 다양하다.

- 직접 반복문을 수행하면서 값을 대입하는 방법

- 선언과 동시에 값을 대입하는 방법 => int[] arr = {1, 2, 3, 4, 5};

- Arrays.fill()를 이용하는 방법 => Arrays.fill(arr, 1); - 모든 값을 1로 초기화

 

 

2차원 배열은

int[][] arr = new int[4][3];

int[][] arr = new int[4][3];

이렇게 선언하고 초기화할 수 있다.

위의 배열은 3의 크기를 갖는 int형 배열 4개가 들어갈 수 있는 크기의 2차원 배열이다.

arr = [[0, 0, 0],[0, 0, 0],[0, 0, 0],[0, 0, 0]] 으로 나타낼 수 있다.

즉, arr이라는 배열은 int 배열이라는 자료형을 가지고 있는 배열인 것이다.

 

타입 추론, var


추론이라는 단어는 추측과 유사한 단어로 밝혀지지 않은 사실에 대해 논리적인 과정을 밑바침으로 추측하고 예상하는 행위이다.

타입 추론이란, 말 그대로 타입(자료형)을 추론한다고 생각하면 된다.

정적 타이핑을 지원하는 언어에서, 타입이 정해지지 않은 변수에 대해서 컴파일러가 변수의 타입을 스스로 찾아낼 수 있도록 하는 기능이다.

 

- 컴파일러란 작성한 코드를 컴파일해주는 컴퓨터의 소프트웨어로 사람이 작성한 소스코드를 컴퓨터가 알아볼 수 있게 변환해주는 역할을 하며 이외에도 다양한 기능들을 수행한다.

즉 타입 추론이라는 기능을 이용하면 우리가 지금까지 이야기 했던 변수의 자료형을 코드로 작성하지 않아도 컴퓨터가 그 변수의 자료형을 알아서 찾을 수 있다는 것이다.

 

먼저 타입 추론을 사용하는 이유, 장점은

코드의 가독성을 높여주는 것이다.

명시적으로 변수의 타입을 작성할 필요가 없기 때문에 코드의 길이도 줄어들고 쓸데없는 코드들도 제거할 수 있다.

이는 코드의 전체적인 가독성을 높여주는 효과를 불러올 수 있다.

 

중복된 코드도 줄일 수 있다.

자바의 타입 추론이 구현된 제너릭(Generic)을 이용하면

원래는 변수의 타입마다 같은 코드를 모두 작성해야 했던 것들도

하나로 줄일 수 있다.

 

java 10부터는 var라는 Local Variable Type-Inteface가 추가되었다.

기존에는 제너릭과 람다식에서만 타입 추론을 지원하고 있었다. 특정한 구조와 기능을 이용할 때만 타입 추론 기능을 사용할 수 있었다.

java 10부터는 var를 이용해서 변수에 대한 타입 추론을 지원한다.

 

기존의 코드는

String a = "Hello World!";

 

이렇게 String 이라는 변수의 타입을 반드시 개발자가 코드에 명시를 해야했다.

 

그러나 java 10부터 지원하는 var를 이용하면 위의 코드를

var a = "Hello World!";

이렇게 수정할 수 있다.

변수의 타입을 명시하지 않아도 컴파일러가 알아서 a라는 변수의 초기값, 리터럴을 보고 변수의 타입을 추론하는 것이다.

위에서는 a라는 짧고 의미 없는 변수명을 사용했지만, 만약 변수의 값을 표현하는 변수명을 적절하게 이용한다면

타입을 신경쓰지 않고 변수를 사용할 수 있다.

 

그러나 var를 사용할 때는 주의해야할 점이 있다.

1) var는 초기화값이 있는 지역 변수(Local Variables)로만 선언이 가능하다.

var를 이용한 변수를 선언할 때는

var name;
name = "kim";

이렇게 변수를 초기화할 수 없다.

반드시

var name = "kim";

이렇게 선언과 동시에 초기화를 해야한다.

 

또한 지역 변수로만 사용이 가능하다.

var를 멤버 변수, 메소드의 파라미터(Parameter), 리턴 타입(Return Type)으로 사용할 수 없다.

 

위와 같이, 무조건 var를 사용할 때는 선언과 동시에 초기화해야 하며 지역 변수로 밖에 사용하지 못하기 때문에

var를 사용한 변수의 변수명은 굉장히 가독성이 높아야 한다.

위에서 말한 var의 장점이 단점이 될 수도 있다는 것이다.

변수의 타입을 신경쓰지 않아도 되기 때문에 변수명만 보고 변수를 사용할 수 있지만,

만약 변수명을 난해하고 명확하게 짓지 않는다면 오히려 역효과가 날 수 있다.

 

var의 또 다른 특성은

var는 키워드, 예약어가 아니다.

그러므로  var라는 변수를 선언할 수 있다.

 

var라는 별도의 데이터 타입이 존재하는 것이 아니라, 컴파일러가 소스 코드를 바이트 코드로 변경하는 과정에서

var를 초기화값을 보고 추론한 타입으로 바꾸기 때문에 별도의 예약어가 아닌 것이다.

 

이 특성으로 인해서 var가 시스템의 성능 상 커다란 비효율을 야기하지 않는다는 것을 알 수 있다.

정말 미세한 차이는 있을 수 있지만 컴파일 과정에서 var를 추론한 타입으로 대체하기 때문에

명시적으로 타입을 쓰는 코드와 이후에 다른 점이 없다.

반응형

관련글 더보기