본문 바로가기

컴퓨터/Java

변수

변수를 사용하려면 먼저 변수를 선언해야한다.

변수를 선언하는 방법은 변수의 타입, 변수 이름을 입력해주는 것으로 타입에는 정수형, 실수형, 문자형이 있다.

변수를 선언한 이후에 변수를 초기화 해야하는데 변수 초기화는 변수를 사용하기 전에 처음으로 값을 저장하는 것을 의미한다.

class VarEx1{
    public static void main(String[] args){
        VarEx1 instance = new VarEx1();
        int year = 0; // 변수 선언 및 초기화

        System.out.println(year); // 변수 출력
        
        year += 1; // 변수에 1 더함
        
		System.out.println(year); // 변수 출력
    }
}

 

변수의 타입

자료형에는 크게 기본형과 참조형이 있는데 기본형 변수는 실제 값을 저장하고, 참조형 변수는 값이 저장되어있는 주소를 저장한다.

Java에서는 참조형 변수간의 연산이 불가능함으로 실제 연산에 사용되는 것은 모두 기본형 변수이다.

기본형 변수는 변수 앞에 타입을 적어주고, 참조변수는 클래스의 이름을 적어준다.

Date today = new Date(); // Date객체를 생성해서 그 주소를 today에 저장

 

  1byte 2byte 4byte 8byte
논리형 boolean      
문자형   char    
정수형 byte short int long
실수형     float double

기본형의 종류와 크기는 위와 같다.

상수와 리터럴

상수는 값을 저장할 수 있지만 변수와 다르게 저장을 하면 다른 값으로 변경할 수 없다.

Java에서 상수는 아래와 같이 final로 선언을 하면 된다.

class VarEx1{
    final float MAX_SPEED = 100_000_000;    
}

상수의 이름은 모두 대문자로 하는게 암묵적인 관례이고 여러 단어로 이루어져있는 경우 '_'를 사용하여 구분할 수 있다.

리터럴은 그 자체로 값을 의미하는 것으로 상수는 값을 저장하면 변경할 수 없는 저장공간이라는 뜻이여서 상수라는 단어로 상수에 해당하는 값을 나타낼 수 없기때문에 리터럴이라는 용어를 사용하였다.

즉, 상수는 리터럴에 의미있는 이름을 붙여 코드의 이해와 수정을 쉽게 한다고 생각하면 된다.

  

리터럴 타입과 접미사

long타입의 경우 접미사 'l' 또는 'L'을 붙이고 접미사가 없으면 int타입으로 지정이 된다. 

int octNum = 010; // 8진수
int hexnum = 0x10; // 16진수
int binnum = 0b10; // 2진수
float pi = 3.14f; // float
double rate = 1.618d; // double

 

타입의 불일치

리터럴의 타입은 저장될 변수의 타입과 일치해야하지만 타입이 달라도 저장범위가 넓은 타입에 좁은 타입의 값을 저장되는것이 허용된다.

int i = 'A'; // 문자 A의 아스키코드인 65가 저장
long l = 123; // int 보다 long의 타입이 더 넓어 저장 가능
double d = 3.14f; // float보다 double이 범위가 더 넓음

 

덧셈 연산자를 이용해 결합을 할 때 피연산자가 모두 숫자일 때는 두 수를 더하지만, 피연산자 중 어느 한쪽이 String이면 나머지 한 쪽을 먼저 string으로 변환한 다음 두 String을 결합한다.

아래 연산의 결과를 통해서 어떤식으로 동작을 하는지 볼 수 있다.

System.out.println(7 + 7 + "");
System.out.println("" + 7 + 7);
System.out.println(7 + "7");

결과:
14
77
77

 

printf()

출력을 할 때 println()을 상요하여 출력을 하면 변수의 값을 그대로 출력하여 값을 변환하지 않고는 다른 형식으로 출력할 수 없다.

이런 경우 printf()를 사용하여 상황에 맞게 여러 형식으로 변환하여 출력할 수 있다.

다만 printf()의 경우 출력 후 줄바꿈을 하지 않아 따로 지시자를 넣어줘야한다.

지시자 설명
%b 불리언
%d 10진수
%o 8진수
%x, %X 16진수
%f 10진수
%e, %E 지수형태표현
%c 문자
%s 문자열

 

System.out.printf("age:%d", age);
System.out.printf("age:%d%n", age);
System.out.printf("age:%d", age);

결과:
age:14age:14
age:14
public class ch2_4 {
    public static void main(String [] args){
        byte b = 1;
        short s = 2;
        char c = 'A';
        int finger = 10;
        long big = 100000000000L;
        long hex = 0xFFFFFFFFFFFFFFFFL;
        int octNum = 010;
        int hexnum = 0x10;
        int binnum = 0b10;

        int i = 'A';
        long l = 123;
        double d = 3.14f;

        System.out.printf("b = %d%n", b);
        System.out.printf("s = %d%n", s);
        System.out.printf("c = %c,  %d %n", c, (int)c);
        System.out.printf("finger = [%5d]%n", finger);
        System.out.printf("finger = [%-5d]%n", finger);
        System.out.printf("finger = [%05d]%n", finger);
        System.out.printf("big = %d%n", big);
        System.out.printf("hex = %#x%n", hex);
        System.out.printf("hex = %x%n", hex);
        System.out.printf("hex = %X%n", hex);
        System.out.printf("octNum = %o, %d%n", octNum, octNum);
        System.out.printf("hexnum = %x, %d%n", hexnum, hexnum);
        System.out.printf("binnum = %s, %d%n", Integer.toBinaryString(binnum), binnum);

        String url = "longshortlongshort";
        System.out.printf("[%.5s]%n", url);

    }
}

결과:
b = 1
s = 2
c = A,  65 
finger = [   10]
finger = [10   ]
finger = [00010]
big = 100000000000
hex = 0xffffffffffffffff
hex = ffffffffffffffff
hex = FFFFFFFFFFFFFFFF
octNum = 10, 8
hexnum = 10, 16
binnum = 10, 2
[longs]

화면에서 입력받기 Scanner

화면에서 값을 입력받아서 해당 값을 사용해야하는 경우가 있다.

이를 위해서는 먼저 아래와 같이 Scanner클래스 객체를 생성하고, 메서드 호출을 통해 값을 입력받고 입력한 내용을 문자열로 저장해서 사용한다.

import java.util.*;

public class ch2_4 {
    public static void main(String [] args){
        Scanner scanner = new Scanner(System.in);
        System.out.print("정수 입력 > ");
        String input = scanner.nextLine();
        int num = Integer.parseInt(input);
        System.out.println("입력 : " + input);
        System.out.printf("num = %d%n", num);

    }
}

결과:
정수 입력 > 10
입력 : 10
num = 10

기본형

논리형

논리형에는 boolean 한 가지 밖에 없다. boolean형 변수는 true와 false 중 하나를 저장할 수 있고, 기본값은 false이다.

 

문자형

문자형은 char 한 가지 자료형밖에 없다. 문자형은 문자가 저장되는게 아니라 문자의 유니코드가 저장되는것이기 때문에 문자가아닌 해당 문자의 코드를 직접 변수에 저장해도 동일하게 저장이 된다.

 

특수 문자

특수 분자 문자 리터럴
tab \t
backspace \b
form feed \f
new line \n
carriage return \r
역슬래쉬 \\
작은따옴표 \'
큰따옴표 \"
유니코드 문자 \u

 

정수형

정수형에는 byte, short, int, long으로 총 4개의 자료형이 있고 각 자료형이 저장할 수 있는 값의 범위가 서로 다르다.

따라서 해당범위를 넘어서는 값에 대해서는 오버플로우가 발생할 수 있다.

아래의 코드와 결과를 통해서 해당 상황에서 어떤 식으로 동작이 되는지 확인할 수 있다.

class OverflowEx {
    public static void main(String [] args){
        short sMin = -32768;
        short sMax = 32767;
        char cMin = 0;
        char cMax = 65535;

        System.out.println("sMin = " + sMin);
        System.out.println("sMin - 1 = " + (short) (sMin - 1));
        System.out.println("sMax = " + sMax);
        System.out.println("sMax + 1 = " + (short) (sMax + 1));
        System.out.println("cMin = " + (int) cMin);
        System.out.println("cMin - 1 = " + (int) -- cMin);
        System.out.println("cMax = " + (int) cMax);
        System.out.println("cMax + 1 = " + (int) ++ cMax);

    }
}

결과:
sMin = -32768
sMin - 1 = 32767
sMax = 32767
sMax + 1 = -32768
cMin = 0
cMin - 1 = 65535
cMax = 65535
cMax + 1 = 0

 

실수형

실수형은 float, double로 두 가지의 타입이 있다.

실수의 경우 소수점도 표현해야하기 때문에 얼마나 큰 값을 표현할 수 있는가 뿐만 아니라 얼마나 0에 가깝게 표현할 수 있는가도 중요하다. 

실수도 정수처럼 저장할 수 있는 값의 한계가 있고 이를 넘어서는 범위에 대해서 오버플로우가 발생하는데 실수의 경우 오버플로우가 발생하면 값이 반대 부호로 넘어가는게 아니라 무한대가 된다.

그리고 정수형과 다르게 언더플로우도 있는데 언더플로우는 실수형으로 표현할 수 없는 아주 작은 값이 되는 경우로 이 경우 변수의 값을 0으로 한다.

class FloatEx {
    public static void main(String [] args){
        float f = 9.12345678901234567890f;
        float f2 = 1.2345678901234567890f;
        double d = 9.12345678901234567980d;
        System.out.printf("       123456789012345678901234%n");
        System.out.printf("f  : %f%n", f);
        System.out.printf("f  : %24.20f%n", f);
        System.out.printf("f2 : %24.20f%n", f2);
        System.out.printf("d  : %24.20f%n", d);

    }
}

결과:
       123456789012345678901234
f  : 9.123457
f  :   9.12345695495605500000
f2 :   1.23456788063049320000
d  :   9.12345678901234600000

형변환

서로 다른 타입간의 연산이 필요한 경우가 있는데 이런 경우 연산을 수행하기 전에 타입을 일치시켜야 하는데, 변수나 리터럴의 타입을 다른 타입으로 변환하는 것을 형변환이라고 한다.

형변환은 변환하고자하는 변수나 리터럴 앞에 변환할 타입을 (타입)피연산자 이처럼 괄호와 함께 붙여주면 된다.

class Casting {
    public static void main(String [] args){
        double d = 85.4;
        int score = (int) d;
        System.out.println("score = " + score);
        System.out.println("d = " + d);
    }
}

결과:
score = 85
d = 85.4

 

정수형과 실수형 간의 형변환을 할 때 정수를 실수로 바꾸는 것은 실수형이 정수형보다 더 큰 저장범위를 갖기 때문에 큰 문제 없이 변환을 할 수 있다. 

다만, 실수형의 정밀도 제한으로 인한 오차가 발생할 수 있다.

따라서 10진수로 8자리 이상의 값을 실수형으로 변환할 때는 float가 아닌 double로 형변환을 해야 오차가 발생하지 않는다.

반대로 실수형을 정수형으로 변환하는 경우 실수형의 소수점 이하는 버려지게 된다. 

그리고 실수의 소수점을 버리고 남은 정수가 정수형의 저장범위를 넘는 경우 정수의 오버플로우가 발생한 결과를 출력한다.

class Casting {
    public static void main(String [] args){
        int i = 91234567;
        float f = (float) i;
        int i2 = (int) f;

        double d = (double) i;
        int i3 = (int) d;

        float f2 = 1.666f;
        int i4 = (int) f2;

        System.out.printf("i = %d\n", i);
        System.out.printf("f = %f i2 = %d\n", f, i2);
        System.out.printf("d = %f i3 = %d\n", d, i3);
        System.out.printf("(int) %f = %d\n", f2, i4);
    }
}

결과:

i = 91234567
f = 91234568.000000 i2 = 91234568
d = 91234567.000000 i3 = 91234567
(int) 1.666000 = 1

 

자동 형변환

서로다른 타입간의 대입이나 연산을 할 때 형변환을 거쳐 타입을 일치시키는 것이 원칙이지만, 경우에 따라 편의상의 이유로 형변환을 생략할 수 있다.

이는 컴파일러가 생략된 형변환을 자동적으로 추가해준다. 이때 타입을 컴파일러가 정하는 기준은 기존의 값을 최대한 보본할 수 있는 타입으로 자동 형변환을 해준다.

 

'컴퓨터 > Java' 카테고리의 다른 글

연산자  (1) 2024.11.11
자바 시작하기 전에  (1) 2024.11.07