Logan. 2021. 8. 16. 16:27

학습할 것


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

 

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


JAVA DATA TYPE은 크게 두가지로 나눌 수 있습니다. 기본타입(primitive type)과 참조타입(referenct type)입니다. 우선 소제목에 적혀있는 타입 기본타입인 Primitive type을 알아보겠습니다.

 

기본 타입은 자바에서 제공하는 기본적인 타입들을 이야기하며 총 4가지의 타입을 가지고 있습니다. 

  • 정수타입(byte, short, int, long)
  • 실수 타입(float, long)
  • 논리 타입(boolean)
  • 문자타입(char)

이렇게 크게 네가지의 타입을 가지고 있으며 각각의 타입에는 정수타입처럼 여러가지 단위가 존재합니다. 그렇다면 각 단위가 의미하는 바가 무엇인지 알아보겠습니다.

 

기본형 구분
크기 저장 가능 범위
기본값
bit byte
정수형 byte 8 1 false, true false
short 16 2 '\u0000' ~ '\uffff' (0 ~ 65535) '\u0000'
int 32 4 -128 ~ 127 0
long 64 8 -32,768 ~ 32767 0
실수형 float 32 4 -2,147,483,648 ~ 2,147,483,647 0
double 64 8 -9,223,372,036,854,775,828 ~
9,223,372,036,854,775,807
0L
문자형 char 16 2 1.4E-45 ~ 3.4E38 (1.4 x 10^-45 ~ 3.4 x 10^38) 0F
논리형 boolean 8 1 4.9E-324 ~ 1.8E308 (4.9 x 10^-324 ~ 1.8 x 10^308) 0

값의 범위는 굳이 기억할 필요가 없지만 메모리크기는 기억하는 것이 좋습니다. 이 메모리 크기를 알고 있으면 값의 범위를 대략적으로 알아낼 수 있습니다. 초기값을 설정할 경우에는 OVERFLOW 즉 넘치는 것을 방지하기 위해서 적절할 단위를 지정해주어야합니다. 단, 무작정 큰 타입의 단위를 지정해준다면 메모리 손실(사용하지도 않는 많은 메모리를 차지하고있는)이 발생할 수 있기 때문에 프로젝트를 잘 이해하고 타입을 설정해야합니다.

 

  1. 논리형은 true 또는 false 두가지 값만 표현하면 돼서 1bit 만으로 충분합니다. 하지만 최소단위가 1byte이기 때문에 boolean의 크기는 1byte입니다.

  2. 문자형(char)은 모든 문자를 저장할 때 사용하는 타입입니다. 정확히는 문자뿐만 아니라 이모티콘도 저장할 수 있습니다. 우리는 char이라는 타입의 박스에 문자를 저장한다고 생각하겠지만 정확히는 모든 유니코드를 저장할 수 있습니다.  유니코드는 각구의 모든 문자를 코드번호(코드 값)으로 지정해논 규약인데, 여기에는 한글도 들어가있고 알파벳도 들어가 있습니다. 논외로 아스키 코드의 범위는 0~ 127의 자주 쓰이는 문자로 구성되어있습니다. 이 중에서 기본적으로 4가지 아스키 코드를 외우고 있으면 활용하기 편합니다.(enter = 13, 숫자 0 =48, 대문자 A = 65, 소문자 a = 97)
  3. 정수형(int)
    int 타입은 자바에서 정수를 '연산'하기 위해 만든 타입입니다. int보다 작은 타입으로 설정하여 계산한다 하더라도 결과값은 무조건 int 타입으로 나와야합니다. 여기서 long 타입은 int보다 큰 범위의 값들을 위한 것들을 지정하기 위해있는 타입이다. 조금 더 생각을 확장해보면 자주쓰는 것에 특별함을 더하는 것이 표현에 있어서 간편하게 설정할 수 있습니다. 따라서 long타입의 해당 값을 넣어주기 위해서는 L이라는 단어가 숫자 맨 마지막에 존재해야만 합니다.
  4. 실수형(double)
    double이 더 많이 쓰이기 때문에 자바에서는 실수를 기본적으로 double로 인식하게 되어있습니다. .float타입은 결과 숫자 끝에 f가 붙어야합니다.

 

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


프리미터 타입은 앞에서 본 것과 같이 크게는 4가지, 작게는 8가지 타입으로 구성되어 있습니다. 프리미터 타입은 크기가 정해져있고 실제 값을 저장하는 반면, 레퍼런스 타입은 크기가 정해져있지 않고 값이 저장되어 있는 주소를 값으로 가집니다. 또 하나의 큰 차이점은 프리미티브 타입의 값을 저장하게 되면 해당 값은 stack영역에 저장되지만 레퍼런스타입 같은 경우에는 지정한 변수명에 해당 참조의 주소가 저장되어 stack 영역에 쌓이게 되고 heap영역에 해당 주소에 실제 값들이 저장되게 됩니다.

 

문법상으로 에러가 없지만 실행시켰을 때 에러가 나는 런타임 에러가 발생할 경우가 있습니다. 예를 들어 객체나 배열을 Null 값으로 받으면 NullPointException이 발생하므로 변수값을 넣어야 합니다.

#리터럴(Literal) feat. 상수(constant)

 


리터럴을 보기 전 상수라는 개념을 먼저 알아보는게 좋을 것 같습니다. 

 

우선 상수는 변하지 않는 변수를 뜻합니다.

  • 상수에 넣는 데이터로는 숫자만 오는게 아니라 클래스나 구조체 같은 객체도 올 수 있습니다.
  • 참조변수를 상수로 지정할 때, 참조변수 안 속성의 데이터까지도 변하지 않는다고 생각할 수 있지만, 참조변수 메모리의 주소값이 변하지 않는다는 의미일 뿐, 그 주소가 가리키는 데이터들은 변할 수 있습니다.

이제 리터럴에 대해서 확인해 볼 수 있습니다.

const a =1;

여기서 a는 상수이고 1은 리터럴입니다.

 

보이는 것과 같이 리터럴은 변수에 넣는 변하지 않는 데이터 자체를 의미합니다..

 

  • 상수는 메모리 위치 즉 공간을 의미하며 메모리 값을 변경할 수는 없습니다.
  • 리터럴은 메모리 위치 안에 저장된 값을 의미합니다. 따라서 변경이 가능합니다.

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


변수의 선언

변수의 선언은 간단하다.

int a;
int b = 1;

//변수를 여러개 선언하는 경우
int t, f, g;

 

일반적으로 선언만 하게되면 위의 int a 처럼 타입을 지정하고 원하는 변수명 선언만 해주면 그만입니다. 하지만 초기화를 시키기 위해서는 값을 '=' 대입 연산자를 통해서 대입해 주어야 합니다. 선언만 하는 것은 그렇게 어렵지는 않지만 이 변수에 대한 이름을 짓는 것 즉 네이밍을 하는 것은 여간 어려운 일이 아닙니다. 이를 위해 스탠다드가 존재하는데 이를 우리는 자바 네이밍 컨벤션이라고 부릅니다.

 

논외로 변수명 네이밍 규칙에 대해서 알아 보면 좋은데 정리되어 있는 아주 좋은 글이 있어 대체하려합니다. 

 

https://woowacourse.github.io/tecoble/post/2020-04-24-variable_naming/

 

좋은 코드를 위한 자바 변수명 네이밍

JAVA…

woowacourse.github.io

 

변수 초기화

 - 대입연산자를 통해서 변수에 값을 넣게되면 이전에 존재했던 값은 사라집니다.

 

초기화  [초기화] 

  • 명사 정보·통신 기억 매체의 기존 기억 내용을 제거하여  매체를 새롭게 사용 가능한 상태로 만드는 .

위 처럼 자바에서의 변수 초기화는 단어의 사전적 정의와 비슷한 방식으로 새 값을 대입하게되면 이전의 값은 사라지게 됩니다.

 

 

초기화의 영역에는 세가지를 볼 수 있습니다.

  • 명시적 초기화(explicit initialization)
  • 생성자(constructor)
  • 초기화 블럭(initialization block)
    -인스턴스 초기화 블럭 : 인스턴스 변수를 초기화하는데 사용합니다.
    -클래스 초기화 블럭 : 클래스 변수를 초기화하는데 사용합니다.

명시적 초기화 : 위에서 봤던 것과 같이 가장 간단하고 직관적인 초기화 입니다.

초기화블럭(static 초기화블럭, instance 초기화 블럭)

 

static 초기화블럭

  • 해당 블럭을 가지고 있는 class 파일이 로드됨과 동시에 heap영역에 올려지게 됩니다.
  • static 초기화 블럭에서 static 메서드를 사용이 가능합니다.
    그 이유는 초기에 statc 멤버들이 모두 초기화 작업을 거친후 초기화 내부가 실행되기 때문에 초기화 작업 전에 이미 메서드는 존재합니다.
  • staic 초기화 블럭이 실행 될때는 instance 멤버들은 존재하지 않습니다. 따라서 instance 멤버에 접근할 수 없습니다.

instance 초기화 블럭

  • 인스턴스 초기화 블럭은 인스턴스가 생성될 때마다 수행됩니다.
  • 모든 생성자가 공통적인 내용을 수행해야할 때 작성하여 중복을 최소화할 수 있습니다.
  • 인스턴스를 new 키워드를 통해 생성했을때 수행 순서는 다음과 같습니다.
    1. 인스턴스 멤버 초기화
    2. 인스턴스 초기화 블럭 수행
    3. 생성자 수행
  • 따라서 인스턴스 메서드를 초기화 블럭 및 생성자에서 호출할 수 있습니다.

생성자

인스턴스를 초기화하는 메서드(하지만 일반 메서드와는 형태가 다른다)

클래스이름(타입  변수명, 타입 변수명){
    // 인스턴스 생성 시 수행할 코드,
    // 주로 인스턴스 변수의 초기화 코드를 적는다.
}
  • 생성자의 이름은 클래스이 이름과 같아야 합니다.
  • 생성자는 리턴 값이 없습니다..
  • 모든 클래스는 반드시 생성자를 가져야합니다.
  • 기본 생성자
    모든 클래스에 기본적으로 적용되어야하는 생성자. 만약 아무 생성자도 존재하지 않을 시에는 컴파일러가 this() 생성자를 실행시켜 기본 생성자를 삽입해준다.
  • 매개변수가 있는 생성자
    매개변수가 있는 생성자는 특별할 것은 없지만 항상 기본생성자를 작성해주어야 한다. 이유는 해당 클래스에 기본 생성자가 존재하지 않을 경우 new 연산자를 통해 객체를 생성하고 클래스이름() 생성자로 초기화하는 과정에서 해당 객체(설계도) 내부에 기본 생성자가 존재하지 않음으로 기본 생성자를 사용하지 못하게됩니다. 따라서 클래스이름() 기본 생성자를 작성해주거나 클래스이름(매개변수)를 입력하여 생성해주어야 합니다.

 

변스의 스코프


변수의 스코프란?

프로그램에서 사용되는 변수는 범위를 말합니다. 이때 이 변수의 사용가능 범위를 스코프라고 이야기합니다.

 

자바는 c/c++과 같이 렉시컬 스코프(lexical scope)를 따릅니다. 즉 변수나 메서드가 선언된 위치에 따라서 스코프의 범위가 달라지는 것을 의미합니다. 따라서 자바에는 여러가지 변수가 존재합니다.

 

크게 클래스변수, 인스턴스 변수, 지역 변수로 나눠 볼 수 있습니다.

public Person{
	static String type ="Asian";
     	String color ="People of color"
    
    public void run(){
    	System.out.println(type); // Asian
    	int age =31;
        String type ="European"
        System.out.println(type); // European
        System.out.println(color); // People of color
        
    }
    System.out.println(age); // error
}

 

  • 클래스 변수(static한 변수 : 변수 앞에 static만 추가하면 됨) 
static 멤버들은 클래스가 로드되면 가장 먼저 heap영역에 올라감으로 static한 멤버는 해당 class안의 어디서든 사용이 가능합니다.

클래스 변수는 한번 선언되면 초기화하지 않고 언제라도 바로 사용할 수 있습니다.

한 클래스의 모든 인스턴스들이 공통적인 값을 유지해야하는 속성의 경우, 클래스 변수로 선언하는 것이 좋습니다.

 

  • 인스턴스 변수(인스턴스를 생성할 때만)

인스턴스 변수의 값을 읽어 오거나 저장하려면 먼저 인스턴스를 생성해야 합니다.

 

  • 지역변수

메서드 내에서 선언되어서 메서드 안에서만 선언가능합니다.

만약 지역변수명과 클래스 변수명이 같고 사용 전 두개다 미리 선언된 경우라면 메서드 안에서는 지역변수가 우선권을 가집니다.

 

 

# 라이프타임

스코프가 변수가 영향을 미칠 수 있는 영역이라면, 라이프 사이클은 변수가 영향을 미칠 수 있는 시점이다.
즉, 어느 위치에 어떻게 선언 되었느냐에 따라 어느 시점에 이 변수를 호출할 수 있는가가 달라진다.

  • 인스턴스 변수
    인스턴스 변수인스턴스가 생성될 때부터 메모리에 존재할 때 까지(GC에 의해 수거 되기 전까지) 접근 가능하다.

  • 클래스 변수
    클래스 변수클래스가 로딩될 때 부터 프로그램이 종료 될 때까지 접근 가능하다.

  • 지역 변수 
    지역 변수선언이 되고 초기화 될 때부터 지역이 끝날 때까지(}) 접근이 가능하다.

 

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


프로그램을 작성하다 보면 같은 타입뿐만 아니라 서로 다른 타입간의 연산을 수행해야하는 경우도 생깁니다. 이럴 때 연산을 수행하기 전에 타입을 일치키셔야 하는데, 이때 변수나 리터럴을 다른 타입으로 변환해주는 것이 형변환(casting)입니다.

 

기본적인 형변환은 아주 간단합니다.

 

(타입) 피연산자

 

앞의 (타입)은 형변환 연산자로서 간단히 형변환을 할 수 있도록 도와줍니다. 따라서 지금 보고 있는 형변환 연산자는 그저 피연산자의 값을 읽어서 지정된 타입으로 변환해주고 그 결과를 반환할 뿐입니다.

 

타입 캐스팅과 프로모션의 차이

 

어찌보면 타입 캐스팅과 프로모션은 크게 차이 없이 형변환을 하는 행위입니다. 하지만 가장 큰 차이점은 데이터의 손실을 감수할 것인가?(타입 태스팅)와 데이터의 손실 없이 형변환을 할 것인가(타입 프로모션)의 차이입니다.

 

간단한 예를 위해 코드를 작성해 보겠다.

 

public class Test2 {
    static double value1 = 2.5;
    static int value2 = (int)value1;

    static float value3 = 2.3f;
    static double value4 = value3;


    public static void main(String[] args) {
        System.out.println(value2);        
    }
}

위의 value2 처럼 형변환 연산자를 통해서 강제 형변환을 만들어 주는 것이 타입 캐스팅입니다. 캐스터 연산자를 사용하지 않으면 IDE는 에러를 표시해 줍니다. 이처럼 2.5라는 실수의 값을 정수 타입의 변수에 담으려한다면 '그래 이거 이렇게 담을꺼야'라고 컴퓨터를 안심시켜주어야 합니다. 그렇지 않은 경우에는 value4처럼 자동으로 '타입 프로모션'을 컴파일러가 자동으로 인식해줍니다.

 

배열


https://laboputer.github.io/ps/2017/09/05/array-and-list/

배열은 같은 타입의 여러 변수를 하나의 묶으므올 다루는 것

배열은 가장 먼저 접하게 되는 자료구조 중에 하나입니다. 선언, 생성 부터 특징까지 빠르고 간단하게 살펴 보겠습니다.

 

선언

선언은 변수 선언과 비슷합니다. 다른 점이 있다면 [] 대괄호를 타입 앞에 붙여줌으로써 '나는 배열이다!'라는 것을 명시해 줍니다

int[] 변수명
String[] 변수명
char[] 변수명

 

생성

생성은 배열 자체가 객체이고 메모리의 공간을 차지하기 때문에 일전에 배운 '생성자(constructor)'를 사용하여서 생성해주면 됩니다.

int[] arr;
arr = new int[배열의 길이];
arr = new int[5];

Integer 타입의 배열 참조변수를 선언하고 해당 변수에 new 연산자를 통해서 객체를 생서하고 int[]생성자를 통해서 값을 초기화 시켰습니다. 이렇게 되면 두줄이나 작성해야하는데 개발자들이 이걸 그냥 놔 두었을리가 없습니다.

int[] arr = new int[배열의 길이];

이렇게 작성하면 선언과 생성이 동시에 이루어집니다. 

 

※여기서 주의사항은 배열의 길를 5로 선언했다면 새 배열을 선언해야 길이 변경이 가능합니다. 따라서 배열을 사용할 것이라면 처음 선언할 당시부터 잘 생각해서 길이를 지정하는 것이 중요합니다.

 

우리가 따로 이름을 붙여주지 않았지만 각 배열(node)에는 이름이 존재합니다. 우리는 해당 명을 '인덱스 번호'라고 부릅니다. 우리가 선언했던 배열 arr에 첫번째 노드의 값을 int arrV에 담고싶다면 int arrV = arr[0]이라 코드를 작성하면 됩니다. '제로베이스'라는 말의 의미를 여기서도 써 먹을 수 있습니다. 다들 알고 계시겠지만 프로그래밍 언어에서는 수를 셈할 때 0부터 세는 경우가 거의 대부분입니다. 

 

그렇다면 만약에 내가 선언하지 않은 배열의 길이(개수)를 알고 싶다고 한다면 .length라는 함수를 사용하여 길이를 측정하면 되는데요. 여기서 주의해야할 점은 배열의 길이는 1개부터 세기때문에 마지막 인덱스 +1을 해주어야합니다. 반대로 .length로 길이를 알고 있다면 길이 -1을 해주면 마지막 index번호를 알 수 있습니다. 

 

그리고 배열을 선언하고 사용할 때 자주 발생하는 에러가 ArrayIndexOutOfBoundsException입니다. 배열의 길이와 맞지 않게 범위를 넘었다고 이야기하는 것입니다.

 

그리고 초기화를 빼먹고 갈 수가 없습니다. 배열의 공간에 값을 지정해 주는 것인데 값이 따로 지정되있는 경우에는

상단에 있는 배열로 선언을 해주면 됩니다. 그렇지 않고 특정 규칙 안에서 변하는 것들이라면 아래 for문 처럼 작성하시면 됩니다.

 

int [] arr = {10,20,30,40,50,60,70,80};

int [] arr = new int[8];
for(int i =1; i <arr.length; i++){
	arr[i] = i * 10
}

 

배열을 출력할 때 대부분 for문을 사용하여 출력합니다. 하지만 조금더 간단하게 출력하려면 클래스를 이용하면 됩니다.

Arrays.toString(arr)을 System.out.println()안에 사용하면됩니다. (예외적으로 char 배열의 경우에는 그냥 println으로 출력하셔도 무방합니다.) - String 배열과 char 배열의 차이점은 읽는 것만 가능하고 내용을 변경할 수 없습니다.

 

2차원 배열

이차원 배열은 조금 복잡해 보이지만 간단히 생각해서 한동의 아파트를 생각해보시면 조금 쉬우실 겁니다. 3층 3라인 짜라 아파트를 하나 가지고 있다고 생각하면 1라인에 1층 , 1라인에 2층, 1라인에 3층 ... 처럼 

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

arr = 	[0][0] [0][1] [0][2] [0][3]
	[1][0] [1][1] [1][2] [1][3]

2행 3열짜리 표가 생긴다고 보시면 편합니다. 그리고 삼차원은 2행 3열짜리 아파트 동이 여러개 생긴다고 보시면되고 4차원배열은 2행 3열짜리 아파트 동이 여러개있는 단지가 여러개 있다고 생각하시면 편하십니다.

 

타입 추론 var


이전에 자바를 아주 조금 공부해보긴 했지만 var타입은 javascript 이후에 처음 접해봤습니다. 

#타입추론이란?

 

타입 추론이란 말그대로 타입을 추론해준다는 의미로 보시면 좋을 것 같습니다. 예를 들어

var i = 3;
var j = "String";

이라고 선언 되었다고 봅시다. 자바는 원래 타입을 명시적으로 클리어하게 적용시켜야하는데 일전에 var라는 타입을 배워본 적도 들어본적도 없습니다. 그렇다면 어떻게 에러가 생기지 않고 변수에 담겨 사용할 수 있게 되는 걸 까요? 

java11버전에 등장한 이 var 타입은 리터럴에 따라 컴파일러가 자동으로 타입을 지정해 줍니다. 허나 주의해야하는 것은 컴파일러가 인식하는 명시적 타입이네는 var라는 것이 없습니다. var가 어떠한 리터럴를 담고 컴파일러로 들어가게된다면 컴파일러는 var를 해당 리터럴이 가지고 있는 타입으로 변환하여 인식합니다. 예를들어 var i =3이라고 코드를 작성하면 컴파일러는 int i = 3; 으로 변환시킵니다. 

 

간단히 var의 타입 추론에서 보아야할 특징을 살펴보겠습니다.