본문 바로가기

WhiteShip Java Study : 자바 처음부터 멀리까지

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

선장님과 함께하는 자바 스터디입니다.

자바 스터디 Github

github.com/whiteship/live-study

 

whiteship/live-study

온라인 스터디. Contribute to whiteship/live-study development by creating an account on GitHub.

github.com

나의 Github

github.com/cmg1411/whiteShip_live_study

 

cmg1411/whiteShip_live_study

✍ 자바 스터디할래. Contribute to cmg1411/whiteShip_live_study development by creating an account on GitHub.

github.com

 

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

 

 

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

primitive type : 자바의 기본값.

 

 

 

 

bit 와 허용범위

1 bit 는 한자리 수이다.

컴퓨터는 2진수를 사용한다.

따라서 1bit는 0, 1 두가지를 표현할 수 있다.

 

8 bits 는 8자리다.

00000000 ~ 11111111 까지 2^8 = 256 가지를 표현가능하다.

 

 

양수와 음수

위 표를 보면 byte 자료형은 크기가 1 byte 이다.

표현 범위는 -128 ~ 127.

0을 기준으로 음수와 양수를 딱 나눠서, 256 가지를 표현하고 있다.

 

각 자료형을 2진수로 나타냈을 때, 가장 왼쪽 한자리수를 MSB 라고 한다.

MSB가 1이면 양수, 0이면 음수임을 나타낸다.

 

byte 자료형으로 예를들면,

00000011 = 3 이고

-3 = 11111101 이다.

두 수를 더하면 자리수를 넘는 1을 빼면 00000000이 됨을 볼 수 있다.

 

3을 통해 -3의 2진수를 구하는 방법은 2의 보수 방법을 쓰면 된다.

2의 보수는 1의 보수에 1을 더한 것이다.

1의 보수는 모든 비트를 반대로 뒤집는 것이다.

 

여담으로 LSB 는 제일 오른쪽, 최하위 비트로 홀수인가 짝수인가를 판별할 수 있다.

 

 

boolean은 1bit가 아닌 1byte

우리가 기본 자료형에 값을 저장하게 되면 이 값은 컴퓨터의 메모리에 저장된다.

메모리에 저장된 값을 JVM에서 읽어올 때, 메모리의 주소를 이용하여 가져오게 된다.

 

우리의 메모리는 하나의 주소에 1byte 단위로 값을 가지게 된다.

따라서 boolean 형이 1bit 만큼의 데이터만 사용하지만, 적어도 1byte 의 메모리를 필요로 한다.

 

 

부동 소수점

자바에서는 실수를 IEEE 754 표준인 부동소수점 방식으로 표현한다.

  • 지수 : 소수점의 위치를 표현 (자리수)
  • 가수 : 유효 숫자

또한 부동소수점 방식의 수는 아래와 같이 표현 가능하다.

(가수)E(지수)

지수가 양수라면 지수 * 10
지수가 음수라면 지수의 절대값 * 0.1

ex) 1.7E-3 = 0.0017

 

double은 8 bytes 로 표현 가능하다.

MSB가 1 bit이므로 양수와 음수를 표현하고,

지수부 11 bits는 11 bits 만큼의 자리수,

나머지 52 bits 만큼을 유효 숫자로 표현하는 것이다.

 

예를 들면, double 의 범위는 

2^(-1074) ~ (2-2^(-52))*2^(1023) 이다.

이를 E 를 이용하여 표현하면,

4.9E-324 ~ 1.7976931348623157E308 이 된다.

 

 

http://www.tcpschool.com/java/java_datatype_floatingPointNumber

부동소수점의 오차 )

부동소수점은 2진수로 표현하기 때문에, 1, 2, 3과같은 숫자들은 2진수로 표현할 수 있지만

무한소수 같은 것들은 2진수 가수와 지수표현으로 표현할 수가 없다.

따라서 근사값을 사용하게 된다.

민감한 소수는 BigDecimal 을 사용한다.

codingdog.tistory.com/entry/%EB%B6%80%EB%8F%99-%EC%86%8C%EC%88%98%EC%A0%90-%EC%99%9C-01%EC%9D%84-%EC%A0%80%EC%9E%A5%ED%95%98%EB%A9%B4-%EC%98%A4%EC%B0%A8%EA%B0%80-%EC%83%9D%EA%B8%B8%EA%B9%8C%EC%9A%94

 

부동 소수점 : 왜 0.1을 저장하면 오차가 생길까요?

 부동 소수점은, 가수부와 지수부로 나누어서 저장을 합니다. 즉, (a)*2^b꼴로 저장을 하는데요. 이 때, a는 1보다 크거나 같고, 2보다 작은 실수입니다. 즉, (1.xxx)*2^b 꼴로 저장을 한다는 겁니다. 여

codingdog.tistory.com

 

char 의 범위

자바에서 char 은 2 bytes (16 bits) 이다. 

자바는 유니코드를 지원한다.

유니코드에서 \u0000 ~ \uFFFF 까지가 char의 범위이다.

(F = (십진수)16 = 2^4)

(2^4 네개 = 16 bits)

 

 

Unsigned

unsigned 는 양수만 취급하는 자료형이다.

unsigned int 는 정수에서 양수만 취급하겠다는 것이다.

그렇게 되면 MSB 가 필요가 없다.

따라서 범위는 0 ~ 2^32 가 된다.

 

원래 자바에서는 타 언어와는 달리 unsigned 를 제공하지 않았다.

하지만 Java 8 버전부터 Wrapper 클래스의 메서드를 이용하여 해당 자료형을 만들 수 있게 되었다.

 

 

여러가지 자료형이 있는 이유와 int

정수가 short, int, long로 많은 자료형이 있듯이, 여러가지 자료형이 있는 이유는

메모리를 필요한 만큼 효율적으로 사용하기 위해서이다.

 

그런데 우리 CPU는 int 자료형에서 빠르게 연산하도록 설계되었다고 한다.

따라서 JVM 에서도 4 bytes 보다 작은 short 형과 byte 형에 대해서 int 로 변환하여 연산하도록 되어있다.

 

따라서 short, byte 자료형을 쓰지 않도록 한다. (물론 큰 수는 long, BigInteger 을 써야 한다.)

 

 

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

Reference : 참고, 참조 라는 뜻을 가지고 있다.

JAVA에서 참조타입은 아래의 네종류가 있다.

자바에서 이 참조는 어떤 뜻인가 ?

 

이를 알기 위해선 이전시간에 배웠던 JVM이 자바실행을 위해 할당하는 메모리 영역 RUNTIME DATA AREA 를 알아야 한다.

 

Runtime Data Area

public class main {
    public static void main(String[] args) {

        int a = 1;
        Integer b = new Integer(10);
        Integer c = new Integer(10);


        System.out.println(a);
        System.out.println(b == c);
    }
}

스택 영역은 각 스레드에서 사용하는 변수들이 쌓인다.

 

int 같은 primitive 타입은 변수 a 를 보듯이 값자체가 스택에 쌓인다.

 

하지만 Integer 는 객체이다.

객체는 힙영역에 생성된다고 배웠다.

힙 영역에 b 의 10, c 의 100 이 생성되었다.

그리고 스택의 b, c 값에는 힙 영역의 메모리 주소번지를 가지고 있다.

 

이후 스레드가 끝나면, 주소를 가리키는 것이 없게되고 GC 는 아무도 안가리키는 힙영역의 객체들을 찾아 정리한다.

 

여기서 메모리의 주소값을 가리킨다 = 참조한다 라고 생각하면 된다.

그래서 참조변수 인 것이다.

 

결국 쉽게말하면,

변수 b, c 에는 10, 100 이라는 값이 들어있는 것이 아니라, 메모리의 주소값이 들어있다.

 

따라서 다음과 같은 경우가 생길 수 있다.

User 라는 객체가 있다고 가정해보자.

public class User {
    private List users = new ArrayList<>();
    
    public void deleteUsers() {
    	users = null;
    }
}
User user1 = new User();
User user2 = user1;

user2.deleteUsers();

user1 에는 생성된 객체의 주소값이 들어 있다.

그 주소값을 user2가 그대로 받았다.

 

그렇게 되면 user2.deteUsers() 메소드를 실행해버리면, user1의 정보도 모두 null이 되버린다.

이는 사용자가 의도하지 않은 상황일 가능성이 크며, 항상 주의해야 한다.

 

 

여담이지만, 
Integer 은 -128 ~ 127 까지의 숫자를 캐싱하여 사용한다.

그리고 Integer a = 10; 과 같은 방법으로 선언하면
Integer a = Integer.valueOf(10); 의 방식으로 자동으로 생성된다.

따라서,
Integer a = 10;
Integer b = 10;
를 하게되면, b 를 생성할때, 캐싱된 a를 사용하므로 

System.out.print(a == b);
의 결과가 true 가 나온다.

그래서 -127 ~ 128 의 범위를 벗어난 값으로 위의 테스트를 하거나
위처럼 new 로 새로운 객체를 생성한다.

 

 

 

 

3. 리터럴

Literal : 변하지 않는 값 자체

 

정수면 199 라는 숫자 자체, 문자면 'c' 라는 문자 자체를 리터럴이라 한다.

 

변하지 않는 값 자체 라 하면 상수와 의미가 헷갈릴 수 있는데,

상수는 프로그램 내에서 static final 로 정의되어 사용되는 프로그램의 자료 같은 느낌이라면,

리터럴은 값 자체, 변수에 들어있거나 상수에 들어있거나 한 값을 말한다.

 

다음은 변수에 할당되는 여러가지 리터럴들이다.

 

3-1. 정수 리터럴

int a = 100;
long b = 2200000000L;
int binary = 0B1101; // 이진수 앞에 0B (숫자 0) (십진수로 13)
int octal = 011; // 팔진수 앞에 0 (숫자 0) (십진수로 9)
int hexadecimal = 0x11; // 16진수 앞에 0x (십진수로 17)

int longNumber = 999_999_999; // 긴 숫자를 편히 보기 위해 언더바를 넣는 것도 리터럴

3-2. 실수 리터럴

float f = 5.16f; // float 접미사 f F
double d = 5.17d; // double 접미사 d D
double d2 = 5.17; // 접미사가 없으면 double 로 인식
float f2 = 5.2 // 불가능

3-3. 문자 리터럴

char alph = 'a'; // 문자 리터럴
char korea = 'ㄱ'; // 유니코드 지원
char korea2 = '\u1100'; // 유니코드 지원 (ㄱ)

3-4. 문자열 리터럴

String name = "mingeor";

3-5. boolean 리터럴

boolean istrue = true;
boolean isFalse = false;
boolean sign = 3 > 2; // true 리터럴

 

 

 

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

우선 클래스에 정의되는 모든 변수는 필드라고 한다.

 

멤버 변수 는 클래스 안에서 정의되고, 클래스의 모든 메소드에서 쓸 수 있는 변수이다.

 

멤버 변수에는 public, private 같은 접근 제어자, final 키워드를 붙일 수 있으며,

그중 static 키워드가 붙어서 정적으로 생성되어 사용되는 변수는 클래스 변수 라 한다. (정적 멤버 변수)

static 이 아닌 변수들은 인스턴스 변수 라 한다.

 

지역 변수 는 메소드 안에서 선언되며, 메소드가 끝나면 없어진다.

접근제어자, final 등의 키워드를 쓸 수 없다.

 

 

4-1. 변수명 규칙

  • 대소문자가 구분
  • 길이제한 없음
  • 숫자로 시작 불가
  • 알파벳, 일부 특수문자(_ , $), 숫자만 가능
  • 공백 불가
  • 예약어를 사용불가

4-2. 초기화 방법

public static String name = "mingeor";
// 변수의 선언과 초기화를 한번에

int number;
// 변수의 선언만 이후 사용시 초기화 가능

int a = 1, b = 3;
// 한줄에 여러개 초기화. but Google Style Guide 를 비롯해 여러곳에서 이 패턴은 사용을 지양한다.

 

4-3. 블록 초기화

 

인스턴스 변수 초기화 블록

{
    int block = 1;
    String blockString = "block;
}

 

클래스 변수 초기화 블록

static {
    int movieNumber = 2;
    Movie iornMan = new Movie();
    Movie StarWars = new Movie();
}

 

 

 

 

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

scope : 변수의 영역

 

자바에서 변수의 영역은 중괄호 {} 로 알 수 있다.

변수는 해당 {} 안에서 사용되고, 존재할 수 있다.

 

public class Car {
    
    public static String serialNumber; // 클래스 변수
    
    public int position = 0; // 인스턴스 변수
    
    public void run() {
    	int number = 1; // 지역 변수
    	position += number;
    }
}

위의 Car 클래스를 보면 인스턴스 변수 position 는 Car 이라는 class의 스코프에 속해 있다.

따라서 Car 클래스로 생성된 인스턴스의 모든 메소드에서 사용 가능하다.

 

반면 지역 변수 number 은 run() 의 스코프에 속해 있다.

따라서 run() 안에서만 사용 가능하다.

 

클래스 변수는 정적으로 선언되어 메소드 영역에서 프로그램 전체적으로 쓰인다.

 

 

스코프는 영역 을 정의한다.

영역이란, 그 범위를 벗어나면 그 변수는 없어진다는 것이다.

이를 변수의 라이프타임이라 하고, 이때문에 다음과 같은 일도 가능하다.

public class Car {

    private int position = 0;

	public void run() {
    	int number = 1; // 같은 number 라는 이름의 변수
    	position += number;
    }
    
    public void back() {
    	int number = 1; // 같은 number 라는 이름의 변수
    	position -= number;
    }
}

 

 

변수에 따라 라이프타임 즉 생성시가와 소멸시기를 정리하면 아래와 같다.

클래스 변수는 클래스가 로딩될 때 라고 되어 있는데,

JVM 은 동적 로딩을 통해 클래스가 로딩한다. 클래스가 사용 될 때, 그때서야 바이트코드에서 클래스를 런타임 영역에 할당시킨다.

클래스가 로딩된다는 것은 아래 코드에서 쉽게 이해해보자.

public class main {
    public static void main(String[] args) {
    	Car.position = 3;
        
        Car genesis = new Car();
    }
}

Car 클래스를 실행시키는 메인 메소드에서

클래스 변수를 사용할 때, 앞에 클래스 이름(Car)을 붙인다. 이때 JVM 을 클래스를 로딩한다.

 

또는 객체를 생성할 때, Car 변수명 = new Car() 에서 제일 앞의 Car 을 보고 JVM 은 클래스를 로딩한다.

 

 

 

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

자바의 primitive 자료형에서 boolean 을 제외한 나머지 타입간에는 타입 변환이 가능하다.

이 타입 변환에는 캐스팅, 프로모션이 있다.

 

타입 프로모션 : 표현 범위가 작은 자료형을 큰 자료형으로 넣을 때 일어나는 자동 형 변환

int a = 10;
double b = a;

> b = 10.0

double 형으로는 모든 int 형을 표현할 수 있다. 뒤에 .0을 붙인다면.

double b = a; 에서는 a 가 자동으로 double 로 형변환이 되어 b 로 들어간다.

 

char g = 'ㄲ';
int n = g;
System.out.println(n);

이 코드의 결과는 12594 이다. 문자의 유니코드값으로 자동 형변환 된다.

 

 

 

타입 캐스팅    : 표현 범위가 큰 자료형을 작은 자료형으로 넣을 때 일어나는 명시적 형 변환

double a = 10.5;
int b = (int) a;

> b = 10;

double 은 소수점을 표현하지만, int 는 소수점을 표현할 수 없다.

이렇게 표현 범위가 작은 자료형으로 변환할 때에는 (int) 처럼 명시적으로 변환해줘야 한다.

 

또 0.5가 사라진 것 처럼, 표현하지 못하는 범위만큼의 데이터손실이 일어날 수 있다.

 

 

 

 

 

7. 1차 및 2차 배열 선언하기

배열은 같은 자료형을 여러개 나열하는 객체이다.

배열은 대괄호 [] 를 이용하여 선언한다.

 

1차원 배열 선언

// 선언과 함꺠 초기화
int[] arr = {1, 2, 3}; // 추천
int [] arr1 = {1, 2, 3};
int arr2[] = {1, 3};

// 배열 선언 (초기화 미룸)
int arr3[];

// 고정크기 배열 선언
int[] array = new int[5]; // 크기 5

(추천이라는 주석은 Java Google Style Guide 에서 배열의 선언은 저 방식을 사용하라 되어있어서..)

 

배열도 객체이기 때문에 new 키워드를 사용하여 생성한다.

배열도 객체이기 때문에 힙영역에 데이터가 생성되고, 제일 앞의 값의 주소값을 변수에서 가지고 있다.

arr3 은 아직 초기화가 되지 않았으므로 아무 값이 없다.

이럴때는 null 값으로 명시적으로 초기화할 수 있다.

 

 

2차원 배열 선언

// 크기만 초기화
int[][] a = new arr[2][3];

// 값으로 초기화
int[][] b = {{2, 3}, {5, 7, 8}};

 

2차원배열은 생성방식이 좀 다르다.

이번에도 그림으로 이해해 보자.

 

 

 

c언어처럼 메모리 순서대로 쭉 저장하는게 아니라,

2행 3열이면 힙 영역에 일단 크기가 2인 1차원 배열을 만들고,

각 값에 크기가 3인 1차원 배열의 주소값을 넣어 참조하게 만든다.

 

b처럼 초기화하면 

2 3

5 7 8 처럼 각 행의 크기가 다른 배열이 생길 수 있는데, 이를 지그재그 배열이라고 한다.

 

 

 

8. 타입 추론, var

타입 추론 : 컴파일러가 사용할 변수의 타입을 유추하고 결정하는 것

 

javascript 에서는 var, let, const 를 사용해서 변수를 선언해 왔다.

이 키워드들은 변수형 자리에 위치하여, 변수에 들어가는 타입을 추론하여 자동으로 변수형을 정해준다.

 

쉽게말해

var a = 10;

var b = true;

라고 하면 들어가는 값에 따라 a는 int, b 는 boolean 이 된다.

 

자바에서는 이 var가 없었는데, java 10 버전부터 생겼다고 한다.

var a = 10; // int 로 들어감
var b = "hi"; // String 으로 들어감

 

var

  • 지역변수에서만 쓸 수 있다.
  • 초기화 없이는 쓸 수 없다.
  • 한번 초기화 되면 다른 타입의 값을 넣을 수 없다.
  • null 값은 불가능 (객체타입만 null 이 가능하니까)
  • var 은 키워드가 아님 (변수명으로 var가능)
  • 타입 파라미터 자리에 쓸 수 없다.
  • 매개변수 타입 자리에도 쓸 수 없다.
  • 람다 표현식의 타입으로 쓸 수 없다. (익명함수는 가능)
var test = () -> System.out.println("hi"); // 불가능

var test = 람다표현식의 익명함수; // 가능

 

 

 

타입 추론은 람다식과 제네릭에서도 쓰이는데, 이는 그 부분이 나오면 언급하도록 하겠다.

람다 타입추론

futurecreator.github.io/2018/07/20/java-lambda-type-inference-functional-interface/

 

 

 

 

 

참고:

blog.naver.com/hsm622/222144931396

catch-me-java.tistory.com/19

 

'WhiteShip Java Study : 자바 처음부터 멀리까지' 카테고리의 다른 글

클래스  (0) 2021.01.30
4주차) 과제  (0) 2021.01.30
제어문  (0) 2021.01.28
연산자  (0) 2021.01.28
JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가  (0) 2021.01.13