Programming/Java

자바, 변수, 데이터 타입, 형 변환, 연산자, Scanner API, System.in, 배열, 2차원 배열, 얕은 복사와 깊은 복사

김심슨 2025. 6. 2. 11:52

자바의 메모리 구조

  1. 메서드 영역 : 클래스에 대한 정보 (클래스 이름, 변수, 메서드, static 변수) 저장. 모든 스레드가 공유
  2. 힙 영역 : new 키워드로 생성한 객체들이 저장되는 공간. 모든 스레드가 공유하며, 가비지 컬렉터가 이 영역을 관리함
  3. 스택 영역 : 메서드 호출 시 생성되는 지역 변수들이 저장됨. 각 스레드마다 따로 존재. 메서드 호출이 끝나면 해당 스택은 자동으로 제거
  4. 프로그램 카운터 레지스터 : 현재 실행 중인 JVM 명령어 주소를 저장합니다. 스레드마다 하나씩 존재
  • 인텔리제이에서 자주 사용하는 단축키 Ctrl + J 단축구문 전체 팝업
    sout System.out.println(); 자동완성
    serr System.err.println(); 자동완성
    main / psvm public static void main(String[] args) { } 자동완성
    Alt + Insert 생성자, getter/setter 메서드 자동 생성 (cmd + N on Mac)
    Shift + Enter 커서 위치에 상관없이 다음 줄 생성
    Ctrl + Shift + ↑/↓ 현재 줄 위/아래로 이동 (라인 위치 바꾸기)
    Ctrl + I 상속받은 메서드 자동완성
    Ctrl + Shift + U 대/소문자 전환
    Alt + Enter 오류/경고 위에서 대체 옵션 제안
    F2 다음 오류/경고/제안으로 이동
    Ctrl + B 변수/클래스/메서드 선언부로 이동
    Ctrl + / 현재 줄 주석 처리 (//)
    Ctrl + Shift + / 블록 주석 처리 (/* */)
    Ctrl + Enter 조건문 괄호 입력 후 {} 자동 생성
    Ctrl + Shift + Enter 조건문 전체 문장 자동완성 (if, for 등)
    Ctrl + Alt + L 코드 자동 줄맞춤 (포맷팅)
    Shift + Shift 전체 검색 (클래스, 파일, 심볼 등)
    Ctrl + Y 현재 줄 삭제
    Ctrl + D 현재 줄 복사하여 아래에 붙여넣기
    Ctrl + Alt + O import 문 최적화
    Ctrl + Alt + T 선택한 코드 블록으로 감싸기 (if, try, for 등)
    Ctrl + R 찾은 문자열을 다른 문자열로 바꾸기
    Shift + F6 변수/파일 이름 바꾸기 (리팩토링)
    Ctrl + Alt + S 설정(Settings) 창 열기
    Ctrl + Alt + Shift + S Project Structure 창 열기
    Ctrl + . 접기(Folding) 기능 – 코드 일부를 ...으로 축약

변수

자바에서 변수는 값을 저장하기 위한 이름이 붙은 메모리 공간

프로그램이 데이터를 일시적으로 기억하고 활용할 수 있게 해줌

변수는 사용하기 전에 반드시 선언해야하고, 선언 시에는 자료형 (예 : int, double, String)과 함께 변수 이름을 명시한다.

자바는 정적 타입 언어. 변수의 자료형이 고정됨. 다른 자료형의 값을 저장하려 하면 컴파일 에러 발생

선언된 위치와 사용 방법에 따라 **지역 변수, 멤버 변수(필드), 클래스 변수(static)**로 나눠짐

자바 변수 이름 규칙 (필수)

  1. 영문자, 숫자, 밑줄, 달러 기호 사용 가능
  2. 숫자로 시작할 수 없음
  3. 자바 예약어는 변수명으로 사용할 수 없음

선언과 저장

자료형 변수 이름; → 변수 선언

변수 이름 = 값 → 값 저장(대입)

int age; -> 변수 선언 
double height; -> 실수형 변수 height 선언 
String name; -> 문자열을 저장할 변수 name 선언 

age = 20;
height : 160.5
name = "김사과" 

System.out.println("이름: " + name);
System.out.println("나이:" + age);
System.out.println("키: "+ height);

------------------------------------------

이름: 김사과
나이:20
키: 160.5

데이터 타입

변수에 어떤 종류의 데이터를 저장할 수 있는지 정해주는 설명서 → 변수에 숫자를 저장할지, 글자를 저장할지, 참/거짓을 저장할지 자바에 미리 알려주는 것

예전에는 메모리를 아껴쓰려고 데이터 타입을 잘 지켰다. 요즘은 메모리<속도 이기 때문에

1. 기본형 데이터 타입 (Primitive Types) - 총 8가지 타입

스택에 직접 저장하는 것

  • byte (1바이트) : -128 ~ 127 정수
    • byte a = 100;
  • short(2바이트) : 작은 정수 30000대
    • short b = 30000;
  • ⭐int(4바이트) : 일반적인 정수 타입
    • int c = 100000;
  • long(8바이트) : 큰 정수, 끝에 L 붙이기
    • long d = 1000000000000L;
  • float(4바이트) : 소수점 숫자, 끝에 f
    • float e = 3.14f;
  • double(8바이트) : 더 정밀한 소수
    • double f = 3.141592;
  • char(2바이트) : 한 글자 문자형(매핑되어있는 숫자 가능), 작은따옴표 ‘’ 사용
    • char g = ‘A’;
    • 아스키 코드 : 문자를 숫자로 표현 (매핑되어있음)
  • boolean(1바이트, 논리값) : True, false

참조형 데이터 타입 (Reference Types)

기본형이 아닌 모든 데이터

실제 데이터가 있는 메모리 주소를 변수에 저장함

  • String : 문자열 저장 (한 글자 이상)
    • String name = “홍길동”;
  • 배열 : 같은 타입의 데이터를 여러 개 저장
    • int numbers = {1, 2, 3};

예제 : Ex02_DataType.java

package lesson01;

public class Ex02_DataType {
    public static void main(String[] args) {
//      main :  jvm이 가장 먼저 자동으로 실행시켜주는 메서드, 함수 역할을 하는 것
        int age = 20; // 정수형
        double height = 160.5; // 실수형
        char grade = 'A'; // 문자형 (한 글자)
        boolean isStudent = true; // 관례상 is로 시작하면 논리형
        String name = "김사과"; // 문자열 (참조형)

        System.out.println("이름:" + name);
        System.out.println("나이:" + age);
        System.out.println("키:" + height);
        System.out.println("학점:" + grade);
        System.out.println("학생여부:" + isStudent);

    }
}

----------------------------------------------------------------------
이름:김사과
나이:20
키:160.5
학점:A
학생여부:true

형 변환

변수나 값의 자료형을 다른 자료형으로 바꾸는 과정

숫자 타입 간 (int → double)에는 자동 변환이 가능.

크기가 큰 타입에서 작은 타입으로 바꿀 때에는 **명시적 형 변환(casting)**이 필요함 (double → int)

계산, 저장, 비교 등의 작업에서 타입이 서로 다를 때 사용됨.

잘못 사용하면 데이터 손실 발생할 수 있으니 주의

자동(묵시적) 형 변환

int num = 100;
double result = num;  // int → double 자동 변환
System.out.println(result);  // 출력: 100.0

강제(명시적 형 변환)

괄호 안에 바꿀 자료형 넣어주기!!

double pi = 3.14159;
int intPi = (int) pi;  // double → int 명시적 형 변환
System.out.println(intPi);  // 출력: 3

< 주의해야하는 상황 : 엉뚱한 숫자가 나오기도 함>

int i = 1000;

byte b = (byte) i ;

System.out.println(b); ⇒ ?

예제 : Ex_03TypeCasting.java

package lesson01;

public class Ex_03TypeCasting {
    public static void main(String[] args) {
        int score = 90;
        double average = score;
        System.out.println("자동 형 변환 결과 :" + average);

        double pi = 3.14159;
        int truncatedPi = (int) pi;
        System.out.println("강제 형 변환 결과 : " + truncatedPi);

        char grade = 'A';
        int gradeCode = grade;
        System.out.println("문자 A의 유니코드 값 : " + gradeCode);
//      문자도 아스키 코드이기 때문에 유니코드 값으로 찍힘

        int code = 66; // 'B'
        char letter = (char) code; // 66이 2바이트에는 넣을 수 있긴 함
        System.out.println("유니코드 66에 해당하는 문자는 : " + letter);
    }
}

float(실수 4바이트) vs long(정수 8바이트) : 누가 더 큰 타입일까? ⇒ long < float

크기를 비교할 땐 표현할 수 있는 값의 종류와 범위를 기준으로 봐야함

예제 : Ex04_FloatLong.java

package lesson01;

public class Ex04_FloatLong {
    public static void main(String[] args) {
        long bigNumber = 100000000000L;  // long 타입
        float converted = bigNumber;     // long → float (자동 형 변환, 정밀도 손실)
        System.out.println("long → float 변환: " + converted);

        float pi = 3.14159f;
        long piToLong = (long) pi;
        System.out.println("float -> long 변환:" + piToLong);
    }
}

연산자

계산 / 비교 등을 수행할 수 있도록 도와주는 기호 또는 예약어

산술 연산자

+ 덧셈 3 + 2 5

- 뺄셈 5 - 2 3
* 곱셈 4 * 2 8
/ 나눗셈 5 / 2 2 (정수 나눗셈)
% 나머지 (mod) 5 % 2 1

주의 : 정수끼리 나누면 소수점은 버려짐. 5 / 2 = 2, 소수를 얻으려면 5.0 / 2처럼 하나 이상이 실수여야 함

복합 대입 연산자

+= 더해서 대입 a += 3;

-= 빼서 대입 a -= 2;
*= 곱해서 대입 a *= 5;
/= 나눠서 대입 a /= 2;
%= 나머지 대입 a %= 3;

증감 연산자

++ 1 증가 a++ 사용 후 증가 (후위)

-- 1 감소 --a 사용 전 감소 (전위)

Scanner API

데이터 입력받는 걸 먼저 해볼게요~!

scanner

사용자로부터 키보드 입력을 받을 수 있게 해주는 표준 입력 처리 도구

java.util 패키지에 포함되어 있어서 임포트 해주면 된다.

숫자, 문자열 등 다양한 형식의 데이터를 쉽게 입력받을 수 있도록 도와줌 → 콘솔에서 입력을 받을 때 사용됨

scanner 사용 방법

  1. 먼저 import java.util.Scanner; 선언
  2. Scanner sc = new Scanner(System.in) 으로 객체 생성
  3. sc.nextInt(), sc.nextLine(), sc.nextDouble() 등 다양한 메서드를 사용해서 입력 ㅇ받음
  4. 사용이 끝나면 sc.close();를 호출해서 자원 정리함

메서드 정리

next() 단어 하나 (공백 전까지) 읽음

nextLine() 한 줄 전체 입력 (공백 포함)
nextInt() 정수 입력
nextDouble() 실수 입력
nextBoolean() true/false 입력

예제 : Ex05_Scanner.java

package lesson01;

import java.util.Scanner;

public class Ex05_Scanner {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // 글을 쓸 수 있는 input 객체
        System.out.print("이름을 입력하세요 :");
        String name = sc.nextLine(); // 한 줄을 입력받을거예용!
        System.out.print("나이를 입력하세요 :");
        int age = sc.nextInt();
        System.out.print("안녕하세요, " + name+ "님! 당신의 나이는" + age +"세입니다");
        sc.close();
    }
}

-----------------------------------
이름을 입력하세요 : 김사과 
나이를 입력하세요 :34
안녕하세요,  김사과 님! 당신의 나이는34세입니다

** 주의 사항 **

nextInt() 후에 nextLine()을 바로 쓰면 입력이 꼬일 수 있습니다. 이때는 sc.nextLine()을 한 번 더 호출해서 버퍼를 비워줘야 함

순서 : Line → Int

int age = sc.nextInt();
sc.nextLine();  // 개행 문자 제거
String name = sc.nextLine();

System.in

표준 입력 장치를 의미. 키보드 입력을 받을 때 사용되는 입력 스트림

보통 Scanner, BufferedReader와 함께 사용되어 입력값을 읽음.

바이트 단위로 데이터를 읽음. 텍스트 입력을 더 쉽게 다루기 위해 보통 래퍼 클래스들과 함께 사용됨.

예제 : Ex06_SystemIn.java

package lesson01;

import java.io.IOException;

public class Ex06_SystemIn {
    public static void main(String[] args) throws IOException {
        System.out.print("문자 하나를 입력하세요 :");
        int input = System.in.read(); // 한 글자(바이트)를 읽음
        System.out.println("입력한 문자 :" + (char)input);

    }
}
------------------------
문자 하나를 입력하세요 :R -> 숫자로 변환해서 저장했다가 char로 변환해서 아래에 찍어준 것
입력한 문자 :R
  • 메소드에서 에러 처리 (try catch문 안써도 됨) : throws IOException

배열

같은 자료형의 값들을 여러 개 저장할 수 있는 연속된 공간

한 번 생성되면 크기가 고정(자바스크립트와 다른 점), 각 요소는 인덱스를 통해 접근할 수 있음.

배열을 사용하면 반복문과 함께 데이터를 효율적으로 처리할 수 있음

int[ ] String[ ] 처럼 자료형 뒤에 대괄호를 붙여서 선언함.

배열의 특징

  • 같은 자료형만 저장 가능 (int[ ], String[ ] , double[ ] 등)
  • 크기(길이)를 미리 정해야 함 → 한 번 정해지면 변경 불가
  • 인덱스는 0부터 시작

배열 선언과 생성

자료형 [ ] 배열 이름 = new 자료형[크기];

int[] scores = new int[5]; // 정수형 5개를 저장할 수 있는 배열 생성(공간만 생성)
또는
int[] scores = {90, 80, 100, 70, 40};	// 배열 생성과 동시에 값을 저장

⇒ scores 자체는 배열을 직접 저장하는 게 아니라, 배열이 저장된 메모리의 위치(주소)르 참조하는 것

int[] a = {1, 2, 3};
int[] b = a;           // 배열 a의 참조값을 b에 복사

b[0] = 99;             // b 배열의 첫 번째 값을 변경

System.out.println(a[0]);  // 출력: 99

⇒ a 배열도 같이 바뀜

예제 : Ex07_ArrayInput.java

package lesson01;

import java.util.Scanner;

public class Ex07_ArrayInput {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int[] scores = new int[3];

        System.out.print("첫 번째 점수를 입력하세요 :");
        scores[0] = sc.nextInt();

        System.out.print("두 번째 점수를 입력하세요 :");
        scores[1] = sc.nextInt();

        System.out.print("세 번째 점수를 입력하세요 :");
        scores[2] = sc.nextInt();

        System.out.println("첫 번째 점수 : "+scores[0]);
        System.out.println("두 번째 점수 : "+scores[1]);
        System.out.println("세 번째 점수 : "+scores[2]);

        sc.close();
    }
}
----------------------------------------------------------
첫 번째 점수를 입력하세요 :90
두 번째 점수를 입력하세요 :50
세 번째 점수를 입력하세요 :70

첫 번째 점수 : 90
두 번째 점수 : 50
세 번째 점수 : 70

2차원 배열 (배열 안에 배열이 있는 구조)

1차원 배열을 요소로 가지는 배열. 즉 행(row)마다 각각의 1차원 배열이 따로 존재하는 것

  • 1차원 배열 : int [ ]
  • 2차원 배열 : int [ ] [ ] → 배열의 배열
    • int [ ] [ ] matrix = new int [2] [3]; → 2행 3열 배열
int[][] arr = {
    {1, 2, 3},
    {4, 5, 6}
};

⇒ 2개의 행을 참조한다.

예제 : Ex08_JaggedArray.java

package lesson01;

public class Ex08_JaggedArray {
    public static void main(String[] args) {
        int[][] jagged = new int[3][];

        // 가변 배열
        jagged[0] = new int[] {1,2};
        jagged[1] = new int[] {3,4,5};
        jagged[2] = new int[] {6};

        System.out.println("jagged[0][0] = " + jagged[0][0]);
        System.out.println("jagged[0][1] = " + jagged[0][1]);

        System.out.println("jagged[1][0] = " + jagged[1][0]);
        System.out.println("jagged[1][1] = " + jagged[1][1]);
        System.out.println("jagged[1][2] = " + jagged[1][2]);

        System.out.println("jagged[2][0] = " + jagged[2][0]);

    }
}
-------------------------------------------
jagged[0][0] = 1
jagged[0][1] = 2
jagged[1][0] = 3
jagged[1][1] = 4
jagged[1][2] = 5
jagged[2][0] = 6

얕은 복사와 깊은 복사

  • 얕은 복사 : 객체나 배열의 참조값(주소)만 복사해서 원본과 복사본이 같은 데이터를 공유(한 쪽을 변경하면 다른 쪽에도 영향을 줌)
  • 깊은 복사 : 새로운 메모리 공간을 만들고 원본의 값을 하나하나 복사해서 원본과 복사본이 독립적으로 동작하게 만드는 방식 (서로 영향을 주지 않음)

얕은 복사 (Shallow Copy)

int[] a = {1, 2, 3};
int[] b = a;  // 얕은 복사

b[0] = 99;

System.out.println("a[0] = " + a[0]);  // 99
System.out.println("b[0] = " + b[0]);  // 99

깊은 복사 (Deep Copy)

새로운 공간을 만들고 값만 복사해옴

package lesson01;

public class Ex09_DeepCopy {
    public static void main(String[] args) {
        int[] a = {1,2,3};
        int[] b = new int[3];

        b[0] = a[0];
        b[1] = a[1];
        b[2] = a[2];

        System.out.println("a[0] =" + a[0]);
        System.out.println("b[0] =" + b[0]);
        System.out.println("-------b[0] 값 바꿈----------");

        // b 배열 값 변경
        b[0] = 99;

        // 결과 출력
        System.out.println("a[0] =" + a[0]);
        System.out.println("b[0] =" + b[0]);

    }
}
----------------------------
a[0] =1
b[0] =1
-------b[0] 값 바꿈----------
a[0] =1
b[0] =99