변수
- 하나의 값을 저장할 수 있는 메모리 번지에 붙여진 이름
- 초기값 없이 변수 선언만도 가능 → 메모리 할당되지 않았기 때문에 사용 시에는 초기값 지정해야 함
int value; // 변수 선언 - 간응
참조 타입이 아닌 기본 데이터 타입(primitive data type)인 변수는 값 복사에 의한 전달(pass by value)을 하기 때문에 같은 값이라면 모두 같은 메모리 위치에 저장됨
int x = 3;
int y = 3;
System.out.println(System.identityHashCode(x)); //356573597
System.out.println(System.identityHashCode(y)); //356573597
// 값이 같기때문에 같은 메모리 주소를 갖게되어 같은 해시코드를 출력함
정수타입
- bit : 0과 1이 저장되는 단위 (8bit=1byte)
- byte : -128~127 → -2^7~2^7 → 8bit → 음수 128개, 0 포함 양수 128개 → 총 256개
- int : -2147483648~2147483647 (10자리 수) → -2^31~2^31 → 32bit → 4byte
- short = 2byte / int = 4byte / long = 8byte
long타입
기본적으로 컴파일러는 정수 리터럴을 int값으로 간주하기 때문에 int값 허용범위를 초과하는 리터럴은 뒤에 소문자’l’ 또는 대문자’L’을 붙여주어 long타입임을 컴파일러에게 알려줘야 함
문자타입(char)
- 작은따옴표(’)를 사용
- 유니코드를 직접 사용하여 저장할 수 있음
- 초기값을 빈 문자열을 줄 목적으로 빈 작은따옴표 두 개를 작성하면 컴파일 에러 발생함
→ 안에 스페이스유니코드(32)를 하나 포함하여 초기화해야 함
char var1 = 65; // 10진수 65와 매핑되는 문자:'A'
실수타입
- 부동 소수점 사용
- (+) m x 10^n
- (+) → 부호 / m → 가수 / n → 지수
- 기본적으로 컴파일러는 실수 리터럴을 double값으로 간주하기 때문에 float를 사용하고 싶다면 소문자 f 또는 대문자 F를 붙여주어 float타입임을 컴파일러에게 알려줘야 함
- double이라는 이름이 붙은 이유는 float보다 약 2배의 정밀도를 갖는다는 의미에서 붙여진 것
float : 유효 소수 이하 7자리까지 표현 (4byte)
부호 1bit | 지수 8bit | 가수 23bit |
double : 유효 소수 이하 15자리까지 표현 (8byte)
부호 1bit | 지수 11bit | 가수 52bit |
문자열타입(String)
- 큰따옴표(”) 사용
- 기본타입이 아닌 참조타입임
- 참조타입은 값이 같아도 다른 주소값을 참조하는 데에 반해 String타입은 내부 구조에 의해 같은 값이라면 같은 주소값을 참조함 (단, new연산자를 사용하지 않고 값을 직접 초기화했을 때만 가능)
- new 연산자로 객체를 생성하여 같은 값을 준 경우엔 다른 주소값을 참조하므로 해시코드값이 다르다
String a = "같은값";
String b = "같은값";
String x = new String("같은값");
String y = new String("같은값");
System.out.println(System.identityHashCode(a)); //2003749087
System.out.println(System.identityHashCode(b)); //2003749087
System.out.println(System.identityHashCode(x)); //1324119927
System.out.println(System.identityHashCode(y)); //990368553
연산식에서의 타입변환
정수형 변수 산술
byte x = 30;
byte y = 20;
byte z = x + y; // 컴파일 에러
위의 경우 컴파일 에러가 발생한다.
이유는 산술 연산식에 정수타입이 사용되면 int타입으로 자동타입변환 되기 때문이다.
byte x = 30;
byte y = 20;
byte z = (byte)x + (byte)y; // 응 안돼
그리하여 위의 경우처럼 코드를 작성하게 되면 또 컴파일 에러가 발생한다.(ㅎ)
강제타입변환과 산술의 순서 때문이다.
- x와 y를 byte타입으로 재차 선언해 줌 : 둘 다 byte타입
- byte 타입인 x와 y의 산술 : 산술 된 순간 int타입의 값 50이 출력됨
가장 위에 적은 x+y의 연산 코드의 과정과 똑같기 때문에 컴파일 에러가 발생한다.
따라서 컴파일 에러가 발생하지 않도록 byte 타입으로 출력할 수 있는 방법은 아래와 같다.
byte x = 30;
byte y = 20;
byte z = (byte)(x + y);
- 산술을 먼저함 : int타입의 값 50 출력
- int타입의 값 50을 byte 타입으로 강제 타입변환
위의 방식으로 산술 해주면 컴파일에러에서 벗어날 수 있다.
실수형 변수의 산술
실수형 타입 변수의 산술은 쵸큼 다르다. float형 변수끼리의 산술은 float형으로 잘 출력된다. 하지만 산술의 피연산 변수가 하나라도 double 타입이라면 컴파일러는 다른 피연산 변수들도 double 타입으로 자동타입변환 시킨 후 연산한다. 심지어 정수형 + 실수형으로 산술을 시키면 정수형을 실수형으로 자동 타입 변환 시킨 후 연산한다.
그렇다면 int형의 변수를 산술 시켜 실수타입으로 출력하고 싶다면 어떻게 해야 할까?
int x = 1;
int y = 2;
double = x / y; // 0.5?????????????
위 코드의 정답은 ‘0.0’이다.
자바에서 정수형 끼리의 연산은 항상 int형으로 도출된다. 그리고 정수형의 산술결과가 실수라면 정수부분만 출력되기 때문에 ‘0’으로 출력되고, int타입인 '0’을 double타입으로 저장해야 하므로 ‘0.0’이 출력된다.
double 타입의 ‘0.5’로 출력하기 위해서는 정수형과 실수형의 산술결과가 실수형임을 이용해야 한다.
//방법1
int x = 1;
int y = 2;
double = (double)x / y;
//방법2
int x = 1;
int y = 2;
double = x / (double)y;
//방법3
int x = 1;
int y = 2;
double = (double)x / (double)y;
피연산자의 변수가 하나라도 실수타입이라면 다른 피연산자들도 실수타입으로 자동타입변환되기 때문에 연산의 결괏값을 실수타입으로 받을 수 있다.
결론적으로 자바에서의 산술연산의 특징은 다음과 같다.
- 피연산자가 정수타입이면 연산의 결과는 int타입이다.
- 피연산자가 정수타입이고 그중 하나가 long타입이면 연산의 결과는 long타입이다.
- 피연산자 중 하나가 실수 타입이면 연산의 결과는 실수 타입이다.
String타입 문자열 파싱
파싱 : 데이터를 분해, 분석하여 원하는 형태로 조립하고 다시 빼내는 것
String타입의 ‘숫자형태의 문자열’을 숫자 타입으로 파싱 하는 방법
→ 원하는 숫자타입의 클래스에 있는 parse메소드 사용
String str = "10";
int value = Integer.parseInt(str); // 정수 10 출력
String str1 = "2.5";
double value1 = Double.parseDouble(str1); // 실수 2.5 출력
// boolean형도 간응
String str2 = "true";
boolean value2 = Boolean.parseBoolean(str2); // boolean형 true 출력
반대로 숫자타입 또는 boolean타입을 String타입 문자열로 바꾸고 싶다면 String클래스의 메소드 valueOf()를 사용하면 된다.
int num = 10;
String value = String.valueOf(num);
System.out.println(value + 10); // '1010' 출력
Scanner클래스의 next() 메소드와, nextline() 메소드의 공통점과 차이점
두 메서드의 공통점은 한 줄을 입력받는다는 것이다.
차이점은 nextline() 메소드는 공백문자(스페이스), 개행문자(캐리지리턴, 라인피드)를 포함하여 한 줄을 저장하고, next() 메소드는 입력받은 문자열에 공백문자 또는 개행문자가 포함되어 있다면 그전까지의 문자열만 저장한다는 것이다.
Scanner scanner = new Scanner(System.in);
String value = scanner.nextline(); // '777(엔터)' 입력
System.out.println(value); // '777(엔터)' 출력
String value1 = scanner.next(); // '777(엔터)' 입력
System.out.println(value1); // '777' 출력
이러한 특성으로 인해 아래와 같은 이상현상(?)이 발생한다.
Scanner scanner = new Scanner(System.in);
String value = scanner.next(); // 첫번째 입력 : '777 888'
String value1 = scanner.nextLine(); // 두번째 입력 : 아무것도 입력안했는데 그냥 넘어감(?)
System.out.println(value); // '777' 출력
System.out.println(value1); // ' 888' 출력
위에 설명했던 특성으로 인해 두 번째 입력에서는 입력을 받지 않고 넘어가며, 첫 번째 입력에서 '777 888'을 입력했음에도 '777'만 출력이 되며, 두 번째 입력을 하지 않았는데도 ' 888'이 출력이 되는 이상현상이 발생된다. 그 이유는 아래와 같다.
Scanner scanner = new Scanner(System.in); // 첫번째 입력 : '777(스페이스)888(엔터)'
String value = scanner.next(); // '777' 까지만 변수에 저장됨, 입력받은 나머지 문자열이 다음으로 넘어감
String value1 = scanner.nextLine(); // 두번째 입력 : 자동적으로 '(스페이스)888(엔터)'가 넘어오며 변수에 저장됨
System.out.println(value); // '777' 출력
System.out.println(value1); // '(스페이스)888(엔터)' 출력
이러한 특징은 Scanner클래스의 메소드 중 nextLine() 메소드를 제외한 대부분의 메소드가 가지고 있는 특징이기 때문에 주의해서 사용해야 한다.
'Back-End > Java' 카테고리의 다른 글
Java 예습 (0) | 2023.03.05 |
---|---|
java.base 모듈 (0) | 2023.01.19 |
예외처리 (0) | 2023.01.19 |
댓글