핑구

01. 변수 본문

JAVA 웹 개발/1. JAVA

01. 변수

코딩 펭귄 2022. 5. 9. 21:37
📅 2021.08.05 ~ 2021.08.10

변수 (Variable)

변수 : 데이터(값)를 담아두는 공간 = 상자

 

변수 사용 이유

  1. 변수를 사용하지 않으면 해당 숫자/문자/논리 등이 어떤 의도로 사용되는지 알 수 없어 가독성이 떨어진다.
  2. 변수를 사용하지 않으면 코드 변경/업데이트 시 많은 작업을 필요로 하므로 재사용성이 떨어진다. (유지보수에 어려움이 있다.)
  3. 변수를 사용하면 코드량이 감소한다.

 

변수의 선언

  • 컴퓨터에는 메모리 공간(기억할 수 있는 공간)이 존재한다. 이 공간은 Stack, Heap, Static 영역으로 나누어지는데, Stack 영역에 공간이 만들어지는 것을 변수의 선언이라고 한다.
  • 변수의 선언은 자료형 변수명; 형태로 작성한다. 이때 변수명은 자유롭게 지을 수 있다.

 

자료형

  • 변수에 어떤 값을 담을지에 따라 변수의 크기가 결정되는데, 미리 담을 크기를 지정하여 해당 변수의 크기를 정해 놓는 것이 자료형이다.
대분류 소분류 표현식 크기
논리 - boolean 1byte
문자 문자 char 2byte
문자열 String 참조형
숫자 정수형 byte 1byte
short 2byte
int 4byte
long 8byte
실수형 float 4byte
double 8byte

 

  • 문자(char)는 1글자 문자를 말하며, 문자열(String)은 1글자 이상의 문자를 말한다. 하지만 보통 문자열은 2글자 이상일 경우 사용한다.
  • 정수형은 소수점이 없는 숫자이며, 실수형은 소수점이 있는 숫자이다.
  • int와 double은 각각 정수형, 실수형의 기본값이다.
    1. 정수가 int를 기본으로 채택한 이유 : 컴퓨터가 가장 계산하기 좋은 크기는 4byte인데, int가 4byte이므로 기본형으로 사용한다.
    2. 실수가 double을 기본으로 채택한 이유 : 컴퓨터는 십진수를 입력하는 경우 이진수로 변경하여 읽고, 다시 십진수로 변경하여 출력한다. 실수에서는 float가 4byte이지만, 실수형은 이진수로 변환될 때 계산 결과가 무한대로 반복되는 경우가 있다. 따라서 계산에 오차가 생길 수 있는데, 그렇게 되면 값이 명확하지 않기 때문에 값을 보다 명확하게 만들어주기 위해 더 많은 숫자를 저장할 수 있는 double을 기본형으로 한다.
      ※ 정수의 경우 십진수 → 이진수 변환, 이진수 → 십진수 변환 시 반복되는 경우가 없이 딱 떨어지기 때문에 저장되는 숫자가 많을 필요 없다.
  • 기본 자료형은 8개이며, 기본 자료형을 제외한 나머지 자료형은 모두 참조 자료형이다.
  • 참조 자료형은 자바 api 사이트(https://docs.oracle.com/javase/8/docs/api/)의 All Classes 부분에서 확인할 수 있으며, 클래스가 자료형이므로 더 만들어지는 것 또한 가능하다.

 

데이터 저장 단위

  • 비트(bit) : 데이터를 저장할 수 있는 최소 단위이며, 한 칸에 0 또는 1이 들어갈 수 있다.
  • 바이트(byte) : 비트 8개로 이루어진 단위로 데이터 처리 혹은 문자의 최소 단위이다. (1 byte = 8 bit) 따라서 1 바이트에 들어갈 수 있는 가짓수는 2^8 = 256가지이다.

 

변수 저장 가능 범위

  • char는 문자형이기 때문에 음수가 들어갈 수 없다. 기존에는 아스키 코드를 이용하여 문자를 표현했으나, 아스키코드는 128개의 문자만 표현할 수 있기 때문에 범위가 매우 작아 더 많은 문자를 표현할 수 있는 유니코드를 고안하였다.

아스키코드 문자표

 

유니코드 문자표

  • char의 기본값인 '\u0000'은 공백 문자이다.
  • 숫자형의 기본값 뒤에 붙은 영어 문자(L, d, f)는 구분을 위해서 붙여졌다.float와 double도 동일한 이유로 알파벳을 붙여 구분하지만, double는 실수형의 기본값이기 때문에 생략이 가능하다.
  • 정수의 기본값은 int이고, byte와 short는 int보다 작기 때문에 데이터 손실 없이 int에 저장이 가능하다. 하지만 int보다 큰 범위인 long는 불가능하기 때문에 해당 값이 long라는 것을 명시해 주어야 한다. 따라서 문자 L을 붙여 구분한다. 이때 붙이는 문자는 대소문자를 구분하지 않지만, 소문자 l의 경우 숫자 1과 혼동의 여지가 있으므로 보통 대문자를 사용한다.

 

변수 명명 규칙

1. 대소문자가 구분되며 길이 제한이 없다.

2. 예약어를 사용하면 안 된다.

  • 이때 예약어란 자바가 미리 특정한 용도로 사용할 것임을 지정한 단어로 아래와 같은 예약어들이 존재한다.
  • 이러한 예약어 때문에 사용할 수 있는 단어가 한정적이게 되므로 대소문자를 구분하여 다양한 변수명을 사용할 수 있게 하였다.

자바의 예약어 목록

3. 숫자로 시작해서는 안 된다. (숫자가 중간 혹은 끝에 들어가는 것은 상관없다.)

4. 특수 문자는 '_'와 '$'만 허용하며, 띄어쓰기 또한 사용할 수 없다.

  • '$'는 내부 클래스를 표현할 경우 주로 사용된다.
  • '_'는 주로 사용하지 않으며, 사용되는 경우가 정해져 있다. (상수의 가독성을 위해)

5. 이름에 여러 단어가 들어가는 경우 단어의 첫글자는 대문자로 한다. 단, 변수의 첫 시작 글자는 소문자로 한다. (ex. userName)

  • 클래스 이름은 무조건 대문자로 시작하여야 한다.
  • 1~4번의 규칙은 필수 규칙이므로 지키지 않는 경우 에러가 발생하나, 5번의 경우 필수 규칙은 아니다. 하지만 관례이므로 지키는 것이 좋다.

 

변수 값 대입

  • 생성한 변수에 값을 저장(대입)하는 행위로 대입 연산자(=)를 사용하여 대입한다. (ex. int a = 10)
  • 오른쪽에 있는 데이터를 왼쪽의 공간에 대입한다. 오른쪽에 숫자나 문자가 아닌 변수가 있는 경우에도 동일하게 값으로 취급하여 오른쪽 변수의 값을 왼쪽 변수에 대입한다.
  • 변수는 하나의 데이터만 보관하기 때문에 마지막에 대입한 값만 보관된다.
    int num1 = 10;
    int num2 = 5;
    // num1: 10
    // num2: 5
    	 
    int num3 = num2;
    // num3: 5
    	 
    num1 = num3;
    // num1: 5
    
  • 위의 코드 마지막 줄에서는 num1 변수 앞에 자료형을 붙이지 않았는데, 이는 이미 선언한 변수이기 때문이다. 이미 stack 영역에 num1이라는 공간이 생성되어 있으므로, 다시 자료형을 붙여 대입하면 동일한 이름의 공간을 만들라는 명령이 된다. 따라서 이미 선언된 공간을 재사용할 경우에는 자료형을 붙이지 않고 사용한다.
  • 변수에 대입되는 숫자, 문자 등의 값 자체(5, 10)는 리터럴이라고 한다. 수학에서는 상수라고 부르지만, 컴퓨터에서의 상수는 다른 의미로 사용되기 때문에 구분을 위해 리터럴이라는 용어를 사용한다.
  • 변수의 초기화는 변수의 대입과 같은 의미이지만, 제일 처음 값을 대입하는 경우에만 초기화라고 한다.

 

변수 값 대입 및 출력

  • char에 값을 대입할 때는 홑따옴표(')를 사용하며, String에 값을 대입할 때는 쌍따옴표(")를 사용한다. String에 하나의 문자만 사용하여도 쌍따옴표를 사용해 대입하여야 한다.
  • main 메소드에 여러 실행문을 추가할 경우 처음 시작 시 너무 많은 기능을 실행하게 되므로 느려질 가능성이 있다. 따라서 실행 메소드와 기능 메소드의 구분이 필요하다.

main 메소드에서 다른 메소드를 실행하는 방법

1. 클래스 앞에 패키지명까지 다 적어 클래스의 풀네임으로 접근한다.

com.first.variable.A_Variable aa = new com.first.variable.A_Variable();
-------------------------- -> 클래스의 풀네임

aa.declareVariable();

2. import를 사용하여 사용할 클래스의 위치를 명시해준다.

import com.first.variable.A_Variable;

A_Variable aa = new A_Variable();

aa.declareVariable();

import를 사용하는 경우에는 코드 작성 후 저장해야 반영 가능하다.

만약 이미 특정 클래스가 import된 상황에서 다른 패키지에 있는 같은 이름을 가진 클래스를 사용해야 할 경우, import 되지 않은 다른 클래스는 풀네임을 사용하여 접근하여야 한다. (동시에 import 불가능)

 

상수

  • 한 번만 담을 수 있는 변수를 의미하며 값을 저장한 이후에는 수정이 불가능하다.
  • 변하지 않는 값이나 수정되어서는 안 되는 값에 사용된다.
  • 상수는 선언 시 자료형 앞에 final 예약어를 붙여 선언하며, 변수와 구분하기 위해 이름을 대문자로 한다.
  • 이름을 대문자만 사용해 작성할 경우 코드의 가독성이 떨어지는 것을 방지하기 위해 단어 사이에 '_'를 사용한다. (ex. USER_NAME)
  • 변수명은 대소문자를 구분하므로 user 변수와 USER 상수는 서로 다른 공간에 저장된다.

 

문자열

  • 쌍따옴표로 묶인 경우 문자열로 인식하며, String 객체를 이용하여 저장한다. 따라서 기본형 변수 선언과 동일하게 값을 저장할 수 있으며, new String을 이용하여 값을 저장할 수도 있다.
    String str = "기차";
    String str2 = new String("기차");
    
    • 문자열은 '+'를 이용해 문자열끼리 연결이 가능하다.
    • 다른 자료형의 앞에 문자열이 있는 경우 다른 자료형들은 모두 문자열로 바뀐다. 하지만 다른 자료형이 문자열 앞에 있는 경우 해당 자료형끼리의 연산이 선행된 후 문자열로 바뀐다.
      String str = "기차" + 123 + 45 + "출발"; // 출력 결과: 기차12345출발
      String str2 = 123 + 45 + "기차" + "출발"; // 출력 결과: 168기차출발
      String str3 = "기차" + (123 + 45) + "출발"; // 출력 결과: 기차168출발
      

 

형변환(Casting)

  • 자료형 변환이라고도 하며 값의 자료형을 바꾸는 것을 의미한다.
  • 컴퓨터는 같은 자료형일 경우에만 대입, 연산이 가능하다. 따라서 자료형이 다를 때 대입, 연산이 필요한 경우에는 형변환을 하여야 한다.
  • boolean의 경우 다른 자료형으로 형변환이 불가능하다.
  • char의 경우 문자형이지만, 숫자형으로 형변환이 가능하다. 그 이유는 문자가 숫자에 매칭되어 있어 서로 호환이 가능하기 때문이다.
  • 형변환은 자동 형변환과 강제 형변환으로 구분된다.
    1. 자동 형변환 : 컴파일러가 자동으로 값의 범위가 작은 자료형을 값의 범위가 큰 자료형으로 변환하는 것을 의미한다.
      범위가 작은 자료형에서 큰 자료형으로 바뀌는 경우에는 값이 손상되지 않기 때문에 자동 형변환이 가능하다.
      하지만 예외적으로 8 바이트인 long은 4 바이트인 float로 자동 형변환이 가능하다. 그 이유는 float가 더 바이트가 작지만 실수 자체가 정수보다 가질 수 있는 값이 더 많기 때문이다.

    2. 강제 형변환 : 값의 범위가 큰 자료형을 값의 범위가 작은 자료형으로 변환하는 것을 의미하며, 강제 형변환 시 데이터 손실이 발생할 수 있다. 
      컴퓨터가 자동으로 범위가 큰 자료형을 범위가 작은 자료형으로 변환하면 데이터가 임의로 변경될 수 있기 때문에 개발자가 강제 형변환을 명시적으로 진행하는 경우에만 강제 형변환이 일어난다. (ex. int intNum = (int)doubleNum;)
      호환이 가능한 자료형들끼리만 가능하다.
  • int에 문자를 저장하는 것이 가능하며, 저장 후 출력하면 해당 문자에 대응되는 숫자가 출력된다. 하지만 문자열은 저장되지 않기 때문에 쌍따옴표는 사용할 수 없다.
    int num = 'A';
    System.out.println("num : " + num); // A에 맞는 int 값이 출력됨
    
  • char에 char 범위(0 ~ 65535) 내의 숫자를 저장하는 것이 가능하며, 저장 후 출력하면 해당 숫자에 대응하는 문자가 출력된다.
    char ch = 97; 
    System.out.println("ch : " + ch); // 97에 대응되는 문자가 출력됨
    
  • char 범위 내에 있는 리터럴은 char 타입에 저장이 가능하지만, int형 변수는 저장된 값이 char 범위 내의 값이라고 하더라도 char에 저장할 수 없다.
    리터럴 또한 int로 인식되는 것은 맞으나, 그 값 자체이므로 char 범위 내에 있으면 저장할 수 있지만, 변수의 경우 해당 변수에 저장된 값이 아니라 해당 변수의 크기만으로 판단하기 때문에 크기가 더 큰 int는 어떤 경우에도 char 타입으로 자동 형변환되지 않는다.
    int num = 'A';
    char ch2 = num; // 에러 발생
    
  • char의 범위에 존재하지 않는 음수를 char에 대입할 수 없다.
    int에 음수를 저장한 후 강제 형변환을 통해 char에 대입.
    하지만 이 경우 아스키코드 63번의 '?'를 출력한 것이 아니라 -97에 대응하는 문자를 찾지 못했다는 의미로 '?'가 출력되는 것이다. 따라서 어떤 음수를 넣든(-1, -20 등) 같은 결과가 출력된다.
    char ch3 = -97; // 에러 발생
    
    int num2 = -97;
    char ch4 = (char)num2; // 대입 가능
    System.out.println("ch3 : " + ch3); // ? 문자가 출력됨
    
  • 작은 자료형과 큰 자료형을 연산하면 작은 자료형은 큰 자료형으로 자동 형변환되고, 결과값 역시 큰 자료형이 된다. 따라서 결과값보다 작은 자료형에 저장하려고 하는 경우 에러가 발생한다. 
    int iNum = 10;
    long lNum = 100L;
    		
    int iSum = iNum + lNum; // 에러 발생
    

 

해결 방안

1. 수행 결과를 long 자료형으로 받기

long lSum = iNum + lNum;

2. long형을 int형으로 강제 형변환 

int iNum1 = iNum + (int)lNum;

3. 수행 결과를 int형으로 강제 형변환 

int iNum2 = (int)(iNum + lNum);

 

출력 메소드

  • System.out.print() : 줄바꿈을 하지 않고 단순하게 출력만 해 준다.
  • System.out.println() : ln은 line을 의미하며 출력 후 줄바꿈을 해 준다.
  • System.out.printf() : f는 format을 의미하며 내가 정해 놓은 형식에 맞춰 줄바꿈을 하지 않고 출력한다. (%d: 정수형, %c: 문자, %s: 문자열, %f: 실수(float, double 모두), %b: 논리형)
  • 줄바꿈이 안 되는 출력 메소드에서 줄바꿈을 해야 할 경우 "%n" 혹은 "\n"을 사용한다.
    "%n"의 경우 줄바꿈 포멧을 의미하므로 printf 매소드에서만 사용 가능하며, "\n"은 어느 곳에서든 사용 가능하다.
    "%n" 혹은 "\n"는 문자열로 인식되므로 반드시 쌍따옴표로 묶어 사용하여야 한다.
  • println은 줄바꿈 문자를 출력해주기 때문에 아무 내용 없이 단독으로 사용이 가능하지만, print는 아무 내용이 없는 경우 출력할 내용이 없기 때문에 단독으로 사용이 불가능하다.
  • 숫자는 %s 형식으로도 출력이 가능하지만, 이때는 숫자로 인식한 것이 아니라 숫자를 문자열로 인식하게 된 것이기 때문에 %d를 사용해 숫자로 출력해주는 것이 바람직하다.

 

입력 메소드 (Scanner)

  • 사용자로부터 데이터를 받는 클래스로 데이터를 입력받아서 사용해야 하는 경우 사용된다.
  • 사용자로부터 값을 입력받아 사용하기 때문에 Scanner sc = new Scanner(System.in) 부분이 없어서는 안 된다.
  • sc.next(), sc.nextLine()은 모두 문자/문자열을 받을 수 있다.
    Scanner sc = new Scanner(System.in);
    sc.nextLine();
    
  • 실행 시 아무 일도 일어나지 않으나, 아무것도 입력하지 않으면 콘솔 창이 계속 실행되고, 종료되지 않는다. 데이터를 입력한 후 엔터를 누르면 sc.nextLine()이 데이터 그 자체가 되는데, 이때 변수에 해당 데이터를 저장하지 않는 경우 데이터는 사라지게 된다.

nextLine()과 next()의 차이

아래 예시를 보면 쉽게 이해할 수 있다.

 

값을 입력한 후 엔터를 입력하면 해당 값은 버퍼라는 공간에 담기게 된다.

Scanner sc = new Scanner(System.in);
		
System.out.print("이름을 입력하세요 : ");
String name = sc.nextLine();

사용자가 데이터(이름)를 입력하면 위와 같이 버퍼에 이름(ex. 홍길동)과 줄바꿈 문자(\n)가 저장된다. 이때 nextLine()을 사용해 데이터를 가져오면 이름과 줄바꿈 문자를 모두 가져간다. 그 후 값을 넘겨줄 때는 줄바꿈 문자는 빼고 넘겨주기 때문에 name에는 "박신우"가 저장된다.

줄바꿈 문자까지 모두 가져갔기 때문에 버퍼에는 아무것도 남지 않는다.

 

System.out.print("나이를 입력하세요 : ");
int age = sc.nextInt(); 

사용자가 그 다음 데이터(나이)를 입력하게 되면 버퍼에 나이(ex. 20)와 줄바꿈 문자(\n)가 저장된다. 이때 nextInt()로 데이터를 가져오면 줄바꿈 문자는 제외하고 가져가게 되어 age에는 20이 저장된다.

버퍼에는 줄바꿈 문자가 남는다.

 

System.out.print("주소를 입력하세요 : ");
String address = sc.nextLine();

이렇게 버퍼에 줄바꿈 문자가 남아 있는 상태에서 nextLine()으로 데이터를 받아오게 되면 해당 메소드는 줄바꿈 문자를 가지고 가기 때문에 데이터를 입력하지 않았음에도 입력이 종료되는 상황이 발생한다. 따라서 주소를 입력할 수 없는 오류가 발생한다.

 

해결 방안 1.

next()를 사용해 엔터(줄바꿈: \n)를 인지하지 못하게 만들어 준다. next()를 사용하는 경우 줄바꿈을 데이터로 인식하지 않기 때문에 버퍼에 남겨져 있던 줄바꿈은 다음 데이터를 받을 때 버려진다. 따라서 계속해서 쌓이지 않는다.

하지만 해당 방법의 경우 띄어쓰기를 구분자로 인식하기 때문에 문자열을 입력할 때 띄어쓰기를 사용하면 모든 데이터를 읽어오지 못하며, 다음 Scanner 사용 시 읽어오지 못한 데이터로 인해 오류가 발생할 가능성이 있다.

 

해결 방안 2.

nextLine()을 추가해 버퍼에 남겨져 있던 줄바꿈 문자를 버려 준다.

System.out.print("주소를 입력하세요 : ");
sc.nextLine(); // 줄바꿈 문자 버리기
String address = sc.nextLine();

 

해결 방안 3.

파싱(Parsing)하기

파싱이란 문자열을 다른 자료형을 바꾸는 것을 말한다. 형변환과 비슷해 보이지만, 차이점은 형변환은 호환 가능한 자료형끼리의 변환이고, 파싱은 호환이 되지 않는 문자열을 숫자로 변환하는 것이다.

해당 상황에서는 숫자를 받아올 때 줄바꿈 문자가 제거되지 않는 것이기 때문에 숫자를 문자열로 받아 파싱한다.

System.out.print("나이를 입력하세요 : ");
String strAge = sc.nextLine(); // 나이를 문자열로 받음
int age = Integer.parseInt(strAge); // 파싱

int뿐만 아니라 boolean, double, float 또한 파싱이 가능하다.

Integer.parseInt()은 숫자 형태의 문자열을 받아와 처리하므로 숫자 형태 이외의 문자열(ex. 20 , 20세, 스무살 등)을 받아오는 경우 에러가 발생한다.

하지만 Boolean.parseBoolean()의 경우 true, false 이외의 값을 넣어도 에러가 발생하지 않으며, 이외의 값을 넣는 경우 false가 저장된다.

 

입력 메소드에서의 char

charAt(int index): char는 Scanner를 통해 바로 받아올 수 없기 때문에 문자열로 받아온 후 charAt 메소드를 사용하여 변환해주어야 한다. charAt은 String 클래스 안에 있는 메소드로, 문자열에서 index 번째에 있는 문자를 반환한다.
이때 컴퓨터는 0부터 숫자를 세기 때문에 index는 0부터 시작하며, 제일 첫 문자의 index가 0이 된다. (zero index)
만약 문자열의 길이를 벗어나는 인덱스를 입력하는 경우 에러가 발생한다.

'JAVA 웹 개발 > 1. JAVA' 카테고리의 다른 글

05. 객체  (0) 2022.05.10
04. 배열  (0) 2022.05.10
03. 제어문  (0) 2022.05.10
02. 연산자  (0) 2022.05.10
00. 프로그래밍 기초  (0) 2022.05.09