본문 바로가기

TIL/Java

[Java] 변수 규칙, 상수(Constant), 오버플로우, 언더플로우, 데이터 형변환(Type Casting)

변수의 명명 규칙

1. 컴파일 에러를 발생시키는 규칙

-동일한 범위 내에서 동일한 변수명을 가질 수 없다.

		int age = 20;
		//int age = 30;	//동일한 변수 명을 가지므로 에러 발생

-예약어 사용이 불가능하다.

		//boolean true = true;
		//int int = 10;

-대/소문자를 구분한다.

위에서 만든 age와 다른 것으로 취급한다.

예약어 True와 다른 것으로 취급한다.

		int Age = 20;
		int True = 10;

-숫자로 시작할 수 없다.

		//int 1age = 20;	//숫자로 시작해서 에러 발생
		int age1 = 20;

-특수문자는 '_'와 '$'만을 허용한다.


2. 개발자들 간의 암묵적 규칙

-길이의 제한은 없지만 적당한 길이의 변수명 작성하는 것이 좋다.

-합성어일 경우 첫 단어는 소문자로 시작하며, 두 번째 단어부터는 대문자로 시작(camel-case)    ex) isTrue

      exception) 자바에서는 클래스명만 유일하게 대문자로 시작한다.

		int maxAge = 20;
		int minAge = 10;

-단어와 단어 사이는 언더스코어(_) 사용 안 함

		String user_name;	//에러가 발생하진 않지만 이렇게 작성하면 안된다.
		String userName;	//camel case로 작성한다.

-한글로 변수명을 짓는 것이 가능하지만 권장하지 않는다.

		int 나이;		//가능하지만 권장하지 않음

-변수 안에 저장된 값이 어떤 의미를 가지는지 알 수 있도록 이름을 짓는다.

		String s;		//변수가 어떤 의미인지 파악하기 어렵다.
		String name;	//문자열 형태의 이름이 저장되겠구나 하는 의도가 파악이 된다.

-전형적인 변수명이 있으면 가급적 사용한다.

		int sum = 0;
		int max = 10;
		int min = 0;
		int count = 1;

-명사형으로 작성한다.

		String goHome;		//가능하긴 하지만 가급적 명사형으로 짓는다.
		String home;

-boolean형은 의문문으로 작성하되 가급적이면 긍정형으로 네이밍한다.

		boolean isAlive = true;
		boolean isDead = false;		//부정형 보다는 긍정형이 더 나은 방식이다.

상수란?

수식에서 변하지 않는 값을 뜻한다. (변하는 값인 변수와 반대)

변하지 않는 값을 저장하기 위한 메모리 공간

-상수의 사용 목적

변경되지 않는 고정된 값을 저장할 목적으로 사용한다.

초기화 이후 값 대입 시 컴파일 에러를 발생시켜 값이 수정되지 못하도록 한다. 예) 수학 공식에 사용되는 수치 등

-사용방법

1. 상수 선언

		final int AGE;

2. 초기화

		AGE = 20;
		//AGE = 30;		//한 번 초기화 한 이후 값을 재 대입하는 것은 불가능하다.

3. 필요한 위치에 상수를 호출해서 사용한다.

-출력 구문에서 사용

		System.out.println(AGE);	//AGE의 값

-필요시 연산식에 호출해서 사용

		System.out.println(AGE * 2);	//AGE의 2배

-대입 연산시 활용

		int sum = AGE;	//대입 연산자의 오른 편에는 기술 가능함

상수의 명명 규칙

변수와 다르지 않으나 개발자들 간의 암묵적 규칙에서 차이가 있다.

-모든 문자는 영문자 대문자 혹은 숫자만 사용

		final int AGE1 = 20;
		final int AGE2 = 30;
		final int age3 = 40;	//소문자로 사용은 가능하지만 변수와 구분하기 힘들기 때문에 만들어진 규칙이다.

-단어와 단어 사이는 언더스코어(_)를 사용

		final int MAX_AGE = 60;
		final int MIN_AGE = 20;
		final int minAge = 30;	//camel case 사용이 가능하지만 역시 변수와 구분되지 않는다.

데이터 오버플로우

자료형 별 값의 최대 범위를 벗어나는 경우 초과한 값을 버림처리 하고 sign bit를 반전시켜 최소값으로 순환시킴

		byte num1 = 127;
        	System.out.println(num1); 	//127
        	num1++;		//1증가
        	System.out.println(num1);	//-128

언더플로우

오버플로우의 반대 개념으로 최소 범위보다 작은 수를 발생시키는 경우 발생하는 현상이다. 

		byte num2 = -128;
		System.out.println(num2);	//128 : byte 의 최소 저장 범위

		num2--;						//1 감소		
		System.out.println(num2);	//127 : byte의 최대 저장 범위

이러한 오버플로우와 언더플로우가 발생한다고 하여 컴파일 에러나 런타임 에러가 발생하지 않는다. 
그렇기에 최대값 혹은 최소값 범위를 고려해서 코드를 작성해야 한다.

 

-일반적으로 많이 사용하는 int형의 최대값은 대략 21억

		int firstNum = 1000000;			//100만
		int secondNum = 700000;			//70만
		
		int multi = firstNum * secondNum;	//7천억이 나와야 함
		
		System.out.println(multi);			//-79669248

이런 현상이 발생해도 그냥 넘기는 경우가 발생할 수 있다.

이런 경우를 논리적 에러라고 표현한다. (디버깅 시 가장 힘든 이유 중 한 가지)

 

-해결 방법

오버플로우를 예측하고 더 큰 자료형으로 결과값을 받아서 처리한다.

		long longMulti = firstNum * secondNum;
		
		System.out.println(longMulti);		//-79669248

이미 오버플로우 처리 된 결과를 가지고 변수에 담기 때문에 위의 결과와 차이가 없다. 
그렇다면 계산이 처리되기 전에 long 타입으로 자료형을 변경해주어야 한다. (강제형변환 이용) 

		 long result = (long) firstNum * secondNum;
		 System.out.println(result);		//정상적으로 7천억이 출력

데이터 형변환 (Type Casting)

값(Data)의 자료형을 바꾸는 것(Boolean 제외)

 

데이터 형변환이 필요한 이유

- 자바에서 다른 타입끼리의 연산은 피연산자들이 모두 같은 타입인 경우 실행할 수 있다.

- 즉, 같은 데이터 타입끼리만 연산을 수행할 수 있다.

 

자동 형변환

컴파일러가 자동으로 값의 범위가 작은 자료형을 값의 범위가 큰 자료형으로 변환

byte -> short(char) -> int -> long -> float -> double

1byte    2byte           4byte  8byte   4byte    8byte 

점점 더 큰 자료형으로 데이터를 옮겨도 문제 없이 자동 형변환 처리 된다.

 

-자동 형변환 규칙 테스트

		byte bnum = 1;
		short snum = bnum;
		int inum = snum;
		long lnum = inum;

-정수끼리의 자동 형변환

연산 시에도 자동으로 큰 쪽 자료형에 맞춰서 계산된다.

		int num1 = 10;
		long num2 = 20;
        	//int result1 = num1 + num2;	//자동으로 큰 쪽 자료형인 long으로 변경 후 계산하기 때문에 int형 변수에 값을 담을 수 없다.
		long result1 = num1 + num2;		//int + long은 서로 다른 자료형이라 데이터 손실이 발생하지 않는 int -> long 변환을 자동 수행 후 연산한다.
        	System.out.println(result1);	//정상 수행됨

-실수끼리의 자동형변환

연산 시에도 자동으로 큰 쪽 자료형에 맞춰서 게산된다.

		float fnum = 4.0f;
		double dnum = fnum;
        	//float result2 = fnum + dnum;
		double result2 = fnum + dnum;
        	System.out.println(result2);

-정수는 실수로 자동 형변환 된다.

정수를 실수로 변경할 때 소수점 자리수가 없어도 실수 형태로 표현이 가능하다. 이 때 데이터 손실이 없기 때문에 형변환이 가능하다.

정수 실제 값을 저장하는 매커니즘을 가진 것과 달리 실수는 지수부와 가수부를 따로 나눠서 작성하기 때문에 바이트 크기보다 훨씬 더 많은 값을 표현할 수 있다.

		long eight = 8;
		float four = eight;		//내부적으로 long에서 float으로 자동형변환됨
        	System.out.println("four : " + four);		//8.0
		
		float result3 = eight + four;
		System.out.println("result3 : " + result3); 	//16.0

-문자형은 int형으로 자동 형변환 된다.char가 가지는 범위 안의 정수는 char형 변수에 기록 가능하다.

		char ch2 = 65;
		System.out.println("ch2 : " + ch2); 		//A

-논리형은 형변환 규칙에서 제외된다.

어느 자료형이든 boolean을 형변환 해서 담을 수 없다.

		boolean isTrue = true;
		//byte b = isTrue;
		//short s = isTrue;
		//long l = isTrue;
		//char c = isTrue;
		//float f = isTrue;
		//double d = isTrue;

강제 형변환

값의 범위가 큰 자료형을 값의 범위가 작은 자료형으로 변환

강제 형변환 시 데이터손실이 발생할 수 있음.

예) (바꿀자료형) 값;

-정수끼리의 강제 형변환

		long lnum = 8L;
		//int inum = lnum;	//데이터 손실 가능성을 컴파일러가 알려준다. (에러발생)
		int inum = (int)lnum;	//변경하려는 자료형을 명시하여 강제형변환을 해야 함
		short snum = (short) inum;
		byte bnum = (byte) snum;

-실수끼리의 강제 형변환실수를 정수로 변경 시 강제 형변환이 필요하다.

		float fnum2 = 4.0f;
		long lnum2 = (long) fnum2;	//float는 4byte, long은 8byte임에도 자동 형변환이 불가능(소수점 자리 이하 데이터 손실 가능성)

-문자형은 int미만 크기의 변수에 저장할 때 강제 형변환이 필요하다.

		char ch = 'a';
		byte bnum2 = (byte) ch;
		short snum2 = (short) ch;	//같은 2byte인데 왜 강제 형변환을 해야할까? 답은 부호비트(sign bit)로 인한 값의 범위가 다르기 때문.

-논리형은 강제 형변환 규칙에서도 제외된다. 

강제 형변환을 해도 전부 에러난다.

		boolean isTrue = true;
		//byte b = (byte) isTrue;

-자동 형변환과 강제 형변환을 이용한 다른 자료형끼리의 연산

다른 자료형끼리의 연산은 큰 자료형으로 자동 형변환 후 연산 처리 된다.

		int inum = 10;
		long lnum = 100;

-방법 1. 두 수의 연산 결과를 int형으로 변환 후 int 자료형 변수에 리턴 받는다.

		int isum = (int)(inum + lnum);

-방법 2. long형 값을 int로 강제 형변환 한다.

		int isum2 = inum + (int) lnum;

-방법 3. 결과 값을 long형 자료형으로 받는다. (자동 형변환 이용)

		long lsum = inum + lnum;

** 주의! int 미만의 연산의 처리 결과는 int형이다.

		byte byteNum1 = 1;
		byte byteNum2 = 2;
		short shortNum1 = 3;
		short shortNum2 = 4;
        
        	//byte result1 = byteNum1 + byteNum2;	//byte + byte => int
		int result1 = byteNum1 + byteNum2;
		//short result2 = byteNum1 + shortNum1;	//byte + short => int
		int result2 = byteNum1 + shortNum1;
		//short result3 = shortNum1 + shortNum2;	//short + short => int
		int reulst3 = shortNum1 + shortNum2;

형변환 시 주의할 점

데이터 손실에 유의해서 사용해야 한다.

-의도하지 않은 데이터 손실

		int inum = 290;
		byte bnum = (byte) inum;
		
		System.out.println(inum);	//290
		System.out.println(bnum);	//34 비트 앞부분 손실로 예측 어려움

-의도한 데이터 손실

		double height = 175.5;
		int floorHeight = (int) height;
		
		System.out.println(height);		//175.5
		System.out.println(floorHeight);	//175 소수점 절삭