멀티캠퍼스

[2026.04.20] TIL - 16일차 반복문 활용과 배열

buckwheat 2026. 4. 21. 00:13

오늘은 반복문의 활용 방법과 배열을 함께 학습했다. 기본적인 for문을 넘어서 중첩 반복문을 사용해 구구단과 별 출력 같은 패턴을 구현해보았고, 난수를 활용해 반복 범위를 정하는 방법도 익혔다. 또한 배열의 개념, 선언과 할당, 저장 구조, 초기화 방법까지 함께 정리하면서 여러 값을 한 번에 관리하는 방식도 배웠다. 단순히 반복만 하는 것이 아니라, 반복문 안에 조건문을 함께 넣어 원하는 위치에 다른 값을 출력하는 방식과 배열을 활용해 데이터를 다루는 방법까지 연습했다.

 

 

1 .  반복문 활용

1) 1~100 사이 난수 구하기와 합계 출력

testFor04()에서는 1부터 100 사이의 임의의 난수 하나를 구한 뒤, 1부터 그 난수까지의 합계를 출력하는 코드를 작성했다.

먼저 Math.random()을 사용하는 방법을 배웠다.

1 | int random = (int)(Math.random()*100)+1;

Math.random()은 0 이상 1 미만의 실수를 반환한다. 여기에 100을 곱하면 0 이상 100 미만의 값이 되고, int로 형변환하면 소수점이 버려져 0부터 99까지의 정수가 된다. 마지막으로 +1을 해주면 범위가 1부터 100으로 바뀌기 때문에 1~100 사이의 난수를 만들 수 있다.

 

2 |  java.util.Random 클래스

Random 클래스는 랜덤 숫자를 만들어주는 클래스이며, nextInt(100)은 0부터 99까지의 정수 중 하나를 반환한다. 따라서 여기서도 1부터 100까지의 숫자를 만들기 위해 +1을 해주어야 한다.

 

3 | ThreadLocalRandom

java.util.concurrent.ThreadLocalRandom.current().nextInt(1, 101)

 

여기서 nextInt(1, 101)은 1 이상 101 미만이라는 뜻이므로, 결과적으로 1부터 100까지의 숫자를 랜덤하게 반환한다.

 

이 과정에서 nextInt()라는 이름이 같더라도 객체에 따라 역할이 다르다는 점도 다시 정리했다. Scanner의 nextInt()는 사용자가 키보드로 입력한 정수를 받아오는 메소드이고, Random의 nextInt()는 랜덤한 정수를 생성하는 메소드이다. 즉, 같은 이름이라도 어떤 객체에서 호출하느냐에 따라 의미가 달라진다.

 

2) 구구단 출력과 중첩 반복문

 

반복문에서 자주 나오는 예제인 구구단 출력도 연습했다. 구구단은 한 단 안에서 1부터 9까지 반복하고, 또 2단부터 9단까지 반복해야 하므로 반복문 안에 반복문을 넣는 중첩 반복문 구조로 작성했다. 이를 통해 바깥 반복문과 안쪽 반복문이 각각 어떤 역할을 하는지 조금 더 이해할 수 있었다.

또한 Scanner로 입력한 줄 수만큼 별을 출력하는 코드도 작성했다. 이 과정을 통해 반복 횟수를 사용자가 입력한 값으로 정할 수도 있다는 점을 다시 확인했다.

 

3) 줄 수와 칸 수가 같은 위치에만 숫자 출력하기

마지막으로 줄 수와 칸 수를 입력받아 *을 출력하되, 행 번호와 열 번호가 같은 위치에서는 숫자를 출력하는 코드도 작성했다.

 

1****

*2***

**3**

***4*

****5

 

예를 들면 위와 같은 형태이다.

 

이 코드는 중첩 반복문 안에 if문을 함께 사용한 예제였다. ij가 같을 때는 i+1을 출력하고, 같지 않을 때는 *을 출력하도록 하여 원하는 위치만 다르게 표시했다. 이를 통해 반복문 안에서 조건문을 활용하면 단순 반복을 넘어서 다양한 출력 형태를 만들 수 있다는 점을 배웠다.


2.  break와 continue

 

break와 continue는 둘 다 반복문의 흐름을 제어하는 명령어이지만 역할은 다르다. break는 특정 조건에서 반복문이나 switch문을 즉시 종료할 때 사용하고, continue는 반복문을 끝내는 것이 아니라 현재 차례만 건너뛰고 다음 반복으로 넘어갈 때 사용한다. 즉, break는 반복 자체를 멈추는 역할이고, continue는 일부 경우만 제외하고 반복을 계속 진행하게 하는 역할이라고 이해하면 된다.

break와 continue

  • break : 반복문 종료
  • continue : 아래 코드 건너뛰고 다음 반복으로 이동

3. 배열

 

1) 개념

배열은 같은 자료형의 여러 값을 하나의 묶음으로 다루는 구조이다. 변수 하나에는 보통 값 하나만 저장할 수 있지만, 배열을 사용하면 여러 값을 한 번에 관리할 수 있다. 배열에 저장된 각 값은 인덱스 번호로 구분하며, 인덱스는 0부터 시작한다.

 

2) 배열 선언과 할당

배열 선언은 배열을 사용하기 위한 변수를 만드는 단계이다. 배열 선언은 다음 두 가지 형태로 가능하다.

  • 자료형[] 배열명;
  • 자료형 배열명[];

배열을 선언한 뒤에는 new를 사용해 실제 배열 공간을 만들어야 한다. 이 과정을 배열 생성 또는 할당이라고 한다. 이때는 반드시 배열 크기를 지정해야 한다.

  • 자료형[] 배열명 = new 자료형[배열크기];
  • 자료형 배열명[] = new 자료형[배열크기];

예를 들면 다음과 같다.

int[] iarr = new int[5];
char carr[] = new char[10];

보통은 배열이라는 의미가 더 잘 보이기 때문에 자료형[] 배열명 형태를 더 많이 사용한다.

 

3) 배열의 저장 구조

배열은 메모리에서 StackHeap 구조로 나누어 생각하면 이해하기 쉽다.
사진의
int[] arr = new int[4];를 보면, 먼저 arr이라는 배열 변수는 Stack 영역에 만들어진다. 이때 arr 안에는 실제 값 4개가 들어가는 것이 아니라, Heap에 생성된 배열의 주소값이 저장된다. 사진에서는 그 주소가 0x1234로 표시되어 있다.

그리고 new int[4]를 실행하면, 정수 4개를 저장할 수 있는 실제 배열 공간이 Heap 영역에 생성된다. 사진에서 arr[0], arr[1], arr[2], arr[3]가 있는 부분이 바로 그 공간이다. 즉, Stack에 있는 arrHeap에 만들어진 배열을 가리키는 역할만 하고, 실제 데이터는 Heap에 저장된다.

쉽게 말하면 arr은 배열 자체가 아니라 배열의 위치를 알려주는 표지판 같은 것이다.
그래서 배열 변수는 값을 직접 가지고 있는 것이 아니라, 배열이 저장된 위치를 참조하는 변수라고 볼 수 있다.

한편 사진의 Static 영역은 프로그램이 실행되는 동안 공통으로 사용하는 데이터가 올라가는 공간이다. main 메소드나 static 변수, static 메소드가 이 영역과 관련이 있다. 다만 배열이라고 해서 무조건 Static 영역에 저장되는 것은 아니다. 배열 변수가 static으로 선언된 경우에만 그 참조 변수가 Static 영역에 저장될 수 있고, 실제 배열 객체는 여전히 Heap 영역에 생성된다.

 

 

4) 배열과 해시코드

배열 변수를 출력하면 실제 값이 바로 보이는 것이 아니라, 배열이 저장된 주소와 관련된 정보가 출력된다. 또 hashCode()를 확인해보면 각 배열이 서로 다른 고유한 값을 가진다는 것을 알 수 있다. 이는 iarrcarr가 각각 서로 다른 배열 객체를 참조하고 있다는 뜻이다.

배열의 해시코드는 배열이름.hashCode();로 확인할 수 있고, 배열의 길이는 배열이름.length로 확인할 수 있다.

여기서 주의할 점은 배열의 길이는 메소드가 아니라 속성이기 때문에 length()가 아니라 length로 사용해야 한다는 것이다.

 

또한 배열 선언 후 크기를 바꾸어 다시 할당하고 해시코드를 확인해보면, 이전과 다른 해시코드가 나오는 것을 볼 수 있다. 이는 기존 배열의 크기가 직접 수정된 것이 아니라, 새로운 배열 객체가 다시 생성되고 그 배열의 주소를 기존 배열 변수가 새로 참조하게 되었기 때문이다. 즉, 배열의 크기를 바꾼 것처럼 보이지만 실제로는 새로운 배열을 만들고 주소값을 덮어쓴 것이라고 이해할 수 있다.

정리하면 배열은 값 자체를 직접 저장하는 구조가 아니라 배열 객체를 참조하는 구조이며, 해시코드가 달라졌다는 것은 새로운 배열 객체를 참조하게 되었다는 뜻이다.

 

배열은 배열이름 = null;을 통해 참조를 끊을 수도 있다. 이 경우 배열 안의 값이 하나씩 삭제되는 것이 아니라, 배열 변수가 더 이상 해당 배열 객체를 가리키지 않게 된다. 이렇게 더 이상 참조하지 않게 된 배열 객체는 이후 GC(가비지 컬렉터)에 의해 메모리에서 정리될 수 있다.

다만 배열에 null을 대입한 뒤 길이나 해시코드를 구하려고 하면 NullPointerException 오류가 발생한다. null은 참조하는 객체가 없다는 뜻이므로, 더 이상 배열을 가리키지 않는 변수로 lengthhashCode() 같은 값을 확인할 수 없기 때문이다. 이 오류는 자바를 공부하면서 자주 보게 되는 대표적인 오류 중 하나이다.

 

5) 배열의 초기화

배열을 선언하고 할당하면, 배열의 각 요소는 자동으로 자료형의 기본값으로 초기화된다. 예를 들어 int 배열은 각 칸이 0으로 초기화되고, String 배열은 참조형이기 때문에 null로 초기화된다. 하지만 실제로 배열을 사용할 때는 원하는 값을 직접 넣어 초기화하는 경우가 많다. 배열 초기화 방법은 크게 다음과 같이 정리할 수 있다.

 

1 | 인덱스를 이용한 초기화

배열의 각 요소에 인덱스를 사용해 값을 하나씩 직접 넣는 방법이다.
배열의 몇 번째 위치에 어떤 값을 넣을지 명확하게 지정할 수 있어서 가장 기본적인 방식이라고 볼 수 있다.

 

2 | for문을 이용한 초기화

배열의 길이만큼 반복하면서 값을 넣는 방법이다.
값이 일정한 규칙을 가지거나, 같은 작업을 여러 칸에 반복해서 해야 할 때 유용하다. 배열과 반복문은 함께 자주 사용되기 때문에 꼭 익숙해져야 하는 방식이다.

 

3 | 선언과 동시에 초기화

배열을 만들면서 값을 바로 넣는 방법이다.
처음부터 넣을 값이 정해져 있을 때 사용하면 편리하다. 코드를 간단하게 작성할 수 있다는 장점이 있다.

정리하면, 배열은 생성만 해도 기본값으로 초기화되지만, 실제로는 인덱스를 이용하거나, for문을 이용하거나, 선언과 동시에 값을 넣는 방식으로 원하는 데이터로 다시 초기화해서 사용하는 경우가 많다.

 

6)문자 배열과 Character.toUpperCase()

 

이 코드에서는 문자 배열을 초기화하는 방법과 문자를 대문자로 변환하는 방법을 함께 확인할 수 있다.

 

먼저 ch[i] = (char)((int)'a' + i); 부분은 a부터 z까지의 문자를 배열에 순서대로 저장하는 코드이다. 자바에서 문자는 내부적으로 문자 코드 값을 가지기 때문에 숫자처럼 다룰 수 있다.

'a'를 정수로 바꾸면 해당 문자 코드 값이 되고, 여기에 반복문의 i를 더하면 다음 문자들의 코드 값이 된다. 그 값을 다시 char로 형변환하면 a, b, c처럼 순서대로 문자를 만들 수 있다. 즉, 이 코드는 문자의 코드 값을 이용해 알파벳을 배열에 자동으로 채우는 방식이다.

 

또한 Character.toUpperCase(ch[i])Character 클래스의 메소드를 사용해 소문자를 대문자로 바꾸는 코드이다.

Character 클래스는 문자와 관련된 기능을 제공하는 클래스이고,

toUpperCase()는 전달받은 문자를 대응되는 대문자로 변환해 반환한다.

따라서 배열에 저장된 소문자 알파벳을 하나씩 꺼내 Character.toUpperCase()에 넣으면 대문자로 바뀐 결과를 바로 출력할 수 있다.

 

ch[i] = (char)((int)'a' + i);는 문자 코드 값을 이용해 알파벳을 순서대로 저장하는 코드이고, Character.toUpperCase(ch[i])는 Character 클래스의 메소드를 이용해 소문자를 대문자로 변환하는 코드이다.