Datatype과 변수

Jeonghyeon Kim·2022년 10월 3일
0

변수(variable)

  • 변하는 값
  • 하나의 값을 저장하는 메모리(Ram)상의 공간
    즉, 변수 이름 = 메모리 공간에 이름 붙여주는 것임. 이 이름을 이용해 저장공간(변수)에 값을 저장하고, 저장된 값을 읽어올 수도 있다. 이 공간에 저장된 값은 변경될 수 있기 때문에 ‘변수’라는 이름을 갖게 되었다.
  • 따라서 변수 이름은 중복될 수 없음.
  • 변수는 소문자로 시작한다.
  • println으로 변수 출력할 때는 쌍따옴표 안 써도 됨. → system.out.println(name);
    ex) String name = “홍길동” → name이라는 변수에 ‘홍길동’이라는 값을 담아주세요
  • 하나의 변수에는 단 하나의 값만 저장할 수 있으므로, 새로운 값을 저장하면 기존의 값은 사라진다.
    String name = "춘식이";
    System.out.println(name);
    
    -------------------------
    console
    
    춘식이

변수의 선언

int     age; //age라는 이름의 변수를 선언
변수타입  변수명
  • 변수 타입: 변수에 저장될 값이 어떤 타입(type)인지 지정하는 것
  • 변수명: 변수가 ‘값을 저장할 수 있는 메모리 공간’이라면, 변수명은 그 메모리 공간에 이름을 붙여주는 것임. 이 이름을 통해 저장공간(변수)에 값을 저장하고, 저장된 값을 읽어올 수도 있다.

변수를 선언하면, 메모리의 빈 공간에 ‘변수타입’에 알맞은 크기의 저장공간이 확보되고, 이 저장공간을 ‘변수명’을 통해 사용할 수 있게 된다.

int num = 10;

광할한 메모리에 4byte(Int) 공간을 마련하고 거기를 num이라고 명명함. 
그리고 거기에 10을 저장해. 근데 2진수로 바꿔서!! 00001010
내가 num 호출하면 다시 10진수로 바꿔서 출력해줘
int num;
num = 10;

long num;
num = 10;

-> 이렇게 똑같은 변수명으로 중복 선언하면 안된다.
데이터 타입이 달라도 변수명이 같으면 오류남. 겹치지 않게 다른 이름으로 바꿔야 함.
위에서 설명했던 것 처럼 변수명을 줘서 메모리를 할당하는건데 그게 겹치면 되겠냐고.

변수의 초기화

💡 변수의 초기화란, 변수를 사용하기 전에 처음으로 값을 저장하는 것을 뜻한다. 선언된 변수에 최초로 값을 대입하는 것.

변수를 선언하면 그 변수를 사용할 수 있게 되는데, 그 전에 반드시 변수를 ‘초기화(initialization)’해야 한다. 메모리는 여러 프로그램이 공유하는 자원이므로 전에 다른 프로그램에 의해 저장된 ‘알 수없는 값(쓰레기값, garbage value)’이 남아있을 수 있기 때문이다.

변수에 값을 저장할 때는 =를 이용한다. 오른쪽의 값을 왼쪽(변수)에 저장하라는 의미이다. 그래서 대입연산자의 왼쪽에는 반드시 변수가 와야 한다.

int age = 27; //변수 age를 선언하고 27로 초기화한다. 선언과 동시에 초기화한 것.
int num; //선언만 한 상태. 이 상태에서 num 사용하면 (호출하면) 에러남. 값이 없는데 뭘 불러와
int num;
num = 10;
num = 20; //이건 초기화 아님.

-> 하나의 값만 저장할 수 있기 때문에, 10은 사라지고 20이 다시 대입된다.
(덮어씌움) 최종적으로 20이 저장된다.

상수와 리터럴(constant & literal)

  • 상수: 변하지 않는 값
    • 변수처럼 ‘값을 저장할 수 있는 공간’이지만, 변수와 달리 한 번 값을 저장하면 다른 값으로 변경할 수 없다.
    • 변수의 타입 앞에 final을 붙여준다.
      final int MAX_SPEED = 10;
    • 상수는 선언과 동시에 초기화해야 한다.
    • 상수의 이름은 대문자 로 하는 것이 암묵적인 관례이다.
    • 여러 단어로 이루어져있는 경우 ‘_’로 구분한다.
  • 리터럴: 변수에 대입하는 상수 데이터 원래 12, 2022, ‘a’와 같은 값이 ‘상수’인데, 프로그래밍에서는 상수를 ‘값을 한 번 저장하면 변경할 수 없는 저장공간’으로 정의하였기 때문에 이와 구분하기 위해 상수를 다른 이름으로 불러야만 했다. 그래서 상수 대신 리터럴이라는 용어를 사용한다. 우리가 기존에 알고 있던 ‘상수’의 다른 이름이라고 생각하면 된다.
    int year = 2021;
    final int MAX_VALUE = 100;
    
    year -> 변수: 하나의 값을 저장하기 위한 공간
    MAX_VALUE -> 상수: 값을 한 번만 저장할 수 있는 공간
    2021, 100 -> 리터럴: 그 자체로 값을 의미하는 것

❓상수가 필요한 이유?

Int triagleArea = (20*10) / 2; //삼각형 면적 구하는 공식
int rectangleArea = 20*10; //사각형 면적 구하는 공식

이 코드를

final int WIDTH = 20; //폭
final int HEIGHT = 10; //높이

Int triagleArea = (WIDTH * HEIGHT) / 2; //삼각형 면적 구하는 공식
int rectangleArea = WIDTH * HEIGHT; //사각형 면적 구하는 공식

이렇게 리터럴에 ‘의미있는 이름’을 붙여 상수를 만들면 코드 이해와 수정이 쉽다. 이전 코드처럼 작성하면 폭, 넓이 달라질 때마다 하나 하나 찾아서 고쳐야되니까 수정이 어려움.


변수의 데이터 타입

data(값)의 type(종류)에 따라 값이 저장될 공간의 크기와 저장형식을 정의한 것이 자료형(data type)이다. 자료형에는 문자형(char), 정수형(byte, short, int, long), 실수형(float, double) 등이 있으며, 변수를 선언할 때는 저장하려는 값의 특성을 고려하여 가장 알맞은 자료형을 변수의 타입으로 선택하면 된다.

기본형과 참조형

  • 기본형 변수 → 실제 값을 저장
  • 참조형 변수 → 어떤 값이 저장되어 있는 주소(memory address)를 값으로 갖는다.

자바는 참조형 변수 간의 연산을 할 수 없으므로 실제 연산에 사용되는 것은 모두 기본형 변수이다.

💡 bloolean은 ture, false 두 가지만 값만 표현 → 가장 작은 크기인 1byte
char는 자바에서 유니코드(2byte 문자체계) 사용 → 2byte
byte는 크기가 1byte라서 byte
int(4 byte)를 기준으로 짧은 short → 2byte / 긴 long → 8byte
float는 실수값을 부동소수점 방식으로 지정하기 때문에 4byte
double은 float의 두배여서 8byte

  1. Primitive Type(원시 타입, 기본 타입, 기본형)

    • 더 이상 분리할 수 없는 데이터를 말함
    • 실제 값(data)을 저장
    • 계산을 위한 실제 값 저장
    1. 숫자

      1. 정수형: 소수점이 없는 숫자

        byte1byte8bit-128 ~ +127bit 다룰 때 주로 사용한다.
        short2byte16bit-32768 ~ +32767C언어와 호환
        int4byte32bit-20억 ~ +20억
        long8byte64bit-2^63 ~ +2^63-1

        기초 참고

        1 TB=1024 GB
        1 GB=1024 MB
        1 MB=1024 KB
        1 KB=1024 Byte
        1 Byte=8 Bit

        부호

        부호6432168421

        다 같은 정수형인데 4가지나 있는 이유? 각 타입마다 저장할 수 있는 값의 범위가 다르므로 저장할 값의 범위에 맞는 타입을 선택하면 된다.

        💡 자바에서는 int를 많이 사용한다. int는 CPU가 가장 효율적으로 처리할 수 있는 타입이기 때문이다.

        💡 4개의 정수형 중, Int형이 기본 자료형(default data type)

        int num;
        num = 10;
        
        long num2;
        num2 = 10L;
        
        long num3;
        num3 = 351234324123412L;
        
        long 쓰려면 숫자 뒤에 L 적어야 컴이 long으로 인식할 수 있다.
        L 안 적고 351234324123412 적으면 int로 인식해서 오류난다. 
        (int는 저렇게 큰 숫자를 담을 수 없음)
      2. 실수형: 소수점이 있는 숫자

        💡 실수형 (double, float)중, double이 기본 자료형(default data type)

        float4byte소수점 7자리까지 표현 가능
        double8byte소수점 15~16자리까지 표현 가능
        float f = 3.25; -> 오류남
        float f = 3.25F; -> 이렇게 써줘야 float으로 인식함.

        float과 double을 구별하기 위해 리터럴 뒤에 대소문자 f를 작성한다.

        F를 적지 않으면, double을 float으로 변환할 수 없다는 오류 메세지가 뜬다. 즉, F를 입력하지 않으면 double로 인식됨을 알 수 있다.

        double이 더 많은 수를 표현할 수 있어서 정밀도도 높음. double을 쓰는게 맘 편함.

        (그런데 은행권 같은 곳에서는 이자율 등 소수점 계산이 중요하다 보니 더 정확한 계산이 필요함. 그래서 자바에서는 BigDecimal 클래스를 활용한다네) 참조

        리터럴에 소수점이나 10의 제곱을 나타내는 기호 E 또는 e, 그리고 접미사 f, F, d, D를 포함하고 있으면 실수형 리터럴로 간주된다.

        자료형실수형 리터럴다른 형태의 동등한 표현
        double10.10.0
        double.100.10
        Float10f10.0f
        float3.14e3f3140.0f
        double1e110.0
        double1e-30.001
    2. 문자형

      char(자료형)2byte문자를 저장하는데 사용되며, 변수에 하나의 문자만 저장할 수 있다.

      리터럴 앞 뒤를 홑따옴표 ‘ 로 감싸야 한다.

      char ch = 'A';
      char ch = '3'; <- 문자일까 숫자일까? 문자임

      더 정확히 말하면 ‘문자’가 저장되는 게 아니라 ‘문자의 유니코드(정수)’가 저장된다. 컴퓨터는 숫자만 아는 바보..

      문자’A’의 유니코드는 65이므로 변수 ch에는 65가 저장된다.

      그래서 문자 리터럴 대신 문자의 유니코드를 집어 넣을 수도 있음. ch에 65를 직접 저장할 수 있는 것임.

      char ch = 65;
      System.out.println(ch);
      
      -----------------------
      console
      A

      이처럼 문자형인 char는 문자를 내부적으로 정수(유니코드)로 저장하기 때문에 정수형과 별반 다르지 않으며, 정수형 또는 실수형과 연산도 가능하다.

    3. 논리형: 참거짓

      boolean1bytetrue, false 중 한 가지의 값만 담을 수 있다. 조건식과 논리적 계산에 사용된다.
      • 기본값은 false이다.
      • 소문자로만 작성해야함
  2. Reference Type(참조 타입, 참조형)

    • primitive type을 제외한 나머지 모두 참조 타입이다.
    • 참조변수 = 사용자 정의 데이터타입
      java가 정의하지 않은 데이터타입을 사용자가 만들어서 쓰겠단 말
    • 참조형 변수는 어떤 값이 저장되어 있는 주소(memory address)를 값으로 갖는다.
    • 참조변수를 선언할 때는 변수의 타입으로 클래스의 이름을 사용하므로 클래스가 참조변수의 타입이 된다. 따라서 새로운 클래스를 작성한다는 것은 새로운 참조형을 추가하는 셈이다.
    • 참조변수는 null 또는 객체의 주소를 값으로 갖는다.
    Scanner sc = new Scanner(System.in);
    
    여기서 Scanner가 클래스명, sc가 변수명, Scanner()가 메서드
    
    여기서 잠깐! 변수 선언 방법이 머라고? 데이터타입 변수명;
    그럼 Scanner sc; 에서 sc가 변수명이니까 Scanner는 데이터 타입이겠네
    이렇듯 클래스=데이터타입이라는 이야기..

    💡 실제로 JRE System Library > java.base > java.util 들어가보면 Scanner.class가 있음
    JRE System Library > java.base > java.lang 에 들어가보면 String.class가 있음

1. String
    - 문자열(문자 배열, 문자 여러 개를 합친 것)
    - 앞뒤를 “ “로 감싸야 한다.
    
String name = "Jane";

형변환 casting

casting, 형태의 변환 등으로 불리며, 데이터 타입 변경을 의미한다.

기본적으로 연산은 같은 데이터 타입간에만 가능하다. 그러나 서로 다른 타입간에도 연산을 해야 하는 상황.. 옵니다.. 반드시..

  • 기본형(primitive type)은 기본형으로만 형변환이 가능하고, 참조형(reference type)은 참조형으로만 형변환이 가능하다.
  • 단, boolean 타입은 형변환이 불가능함을 알아두자.

아래 코드를 보자.

int num = 10;
long num2 = 10L;
이렇게 서로 타입이 다른 두 개의 변수가 선언되었다.

num = num2; -> 강제 형변환
num2 = num; -> 자동 형변환

int numlong num2를 넣는 것은 강제 형변환이고, long num2int num을 넣는 것은 자동 형변환인가. 아래에서 자세히 알아보자.

  1. 형변환
    a. 형변환 방법
    변수 A = (형변환할 데이터타입명) 변수 B;
    b. 강제 형변환
        int num = 10;
        long num2 = 10L;
        
        num = num2; -> 강제 형변환

intlong을 넣을 때, 강제로 형변환을 해줘야 하는 이유는 다음과 같음

int는 4byte, long은 8byte임. 똑같은 정수 10이지만, 8byte를 4byte에 넣는거.. 쉽지않음. 따라서 개발자가 ‘강제’로 ‘형변환’ 해줘야 함.

또한, 큰 타입에서 작은 타입으로 형변환 하는 것이기 때문에, 값 손실(loss of data)이 발생할 수 있다. 값이 변화할 수도 있다는 의미이다.

        int -> byte 형변환
        
        int i = 300;
        byte b = 10;
        
        b = (byte)i;
        //byte는 1byte int는 4byte

밑에 int → byte 형변환하는데 4byte를 1byte에 넣으려니까 00101100까지만 변환되고 나머지는 다 잘려 나가서 값이 손실됨. 결과적으로 i의 값은 300인데 byte로 형변환하니 44가 됨.

c. 자동 형변환

작은 타입에서 큰 타입으로의 변환은 컴파일러가 자동으로 처리해준다.

        int num = 10;
        long num2 = 10L;
        
        num2 = num; -> 자동 형변환

작은 타입을 큰 타입으로 형변환하는 것이므로, 자동 형변환이 가능하다. 당연히 값 손실도 없다.

💡 byte(1byte) → short, char(2byte) → int(4byte) → long(8byte) → float(4byte) → double(8byte)

        예외 1. (예외라기 보다는 부가설명?)
        *겉보기엔 자동 형변환같지만 강제 형변환이 필요한 경우
        
        int num = -10;
        long num2 = 0L; (8byte)
        float f = 3.2F; (4byte)
        
        num2 = f; 넣으면 에러가 난다. 
        4byte -> 8byte인데 왜 에러가 나지?
        
        floatdouble은 소수이다.
        컴은 숫자 1.0000011로 인식한다.
        1.0000021로 인식한다.
        
        1을 표현하기 위한 방법,1을 표현할 수 있는 경우의 수가 더 많은 타입은 뭘까?
        
        int(혹은 long)에서 1을 표현할 방법은 1뿐이다.
        그러나, float, double1.000011.000021.00003도 다 1로 표현할 수 있다.
        1을 표현할 수 있는 경우의 수가 더 많은 것이다.
        반대로 1.00001int가 표현할 수 있는 방법은 없다.
        따라서, floatdoubleintlong을 포함한다고 말할 수 있다.
        
        따라서, float(4byte) -> long(8byte)에는 강제 형변환이 필요하다.
        
        num2 = (long)f; 이렇게
        부가설명 2.
        
        int num = 10;
        double num2 = 3.2;
        
        System.out.println(num + num2);
        원래 intdouble끼리 타입 안맞아서 계산 안 되는데, 위에서 설명했듯이 doubleint를 포함한다.
        따라서 더 큰 것, 즉 포함하는 타입을 자동으로 따라가는 거지.
        결론. intdouble로 자동 형변환되어서 계산이 된다. double + double이 되는거
        int num = 10;
        double num2 = 3.2;
        
        num2 = num2 + num;
        가능? ㅇㅇ 가능. 위에랑 같은 거지.
        num2 = num2 + (double)num;
        자동 형변환이니까 double 생략해도 되는거.
        
        그럼
        
        num = num2 + num;
        이건 가능? ㄴㄴ 에러 뜸.? 
        대입연산자 = 는 가장 마지막에 실행됨.
        그럼 우측의 num2 + num이 먼저 실행 되고 그 값을 왼쪽에 대입하겠지?
        
        한 번 해볼까
        num2 + num의 double 타입으로 나올 거임.
        그리고 int 타입인 num에 double 타입을 대입하는 건데 이게 되겠어?
        
        그래서
        num = (int)(num2 + num);
        이렇게 식 쓰면 에러 안나고 잘 대입된다.
        
        아니면
        num = (int)num2 + num;
        이렇게 해도 에러 안나고 잘 대입됨.
        
        예제 1. (자동 형변환)
        
        char ch = 'a';
        int num = ch;
        
        System.out.println(num);
        -> 97이 콘솔창에 찍힌다.
        
        -----------------------------
        
        예제2. 강제 형변환
        
        num = 60;
        ch = (char)num;
        
        System.out.println(num);
        -> < 가 콘솔창에 찍힌다. 
        예제2.
        
        System.out.println(1 + '1' + "1");
        
        정수 1int 타입(4byte), '1'char 타입(2byte)으로 서로 연산 불가능
        따라서, 작은 타입인 char를 큰 타입인 int로 자동 형변환 해준다.
        char '1'을 숫자로 바꾸면 49가 나온다. (아스키 코드 참조)
        1 + 49 = 50
        여기에 + "1" 하면 최종값
        -> 501

OverFlow & UnderFlow

  1. OverFlow

    • 표현할 수 있는 최대값을 넘어서면, 최소값으로 이동하는 것
    • 해당 타입이 표현할 수 있는 값의 범위를 넘어서는 것
    byte-128부터 127까지 담을 수 있다.
    
    byte a = 127;
    byte b = 1;
    
    byte c = a + b;

    a와 b는 모두 byte 타입으로 연산이 가능하다. 하지만 127+1의 값 128는 byte가 담을 수 없는 숫자이다. 이 때 발생하는 것이 오버플로우이다.

    아래는 127을 이진수로 표현한 것이다.

    01111111

    여기에 +1을 하게 되면? 이진수에서 1+1은 0이므로 결과는

    10000000

    맨 앞에 1이니까 음수. -128이 된다.

  2. UnderFlow

    표현할 수 있는 최소값을 넘어가면, 최대값으로 이동하는 것

남궁성, 『Java의 정석』 도우출판(2016),p20-82

나의 사이버 아버지 남궁성

profile
hello world

0개의 댓글