일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- spring batch
- Spring
- CS
- virtualization
- Java
- 백엔드
- HTTP
- 스프링 배치
- spring cloud
- 데이터베이스
- CI/CD
- mysql
- 스프링
- vm
- ORM
- JPA
- 스프링 부트
- spring boot
- Container
- computer science
- 영속성 컨텍스트
- 스프링 시큐리티
- 배포
- 웹 서버
- 가상화
- 컨테이너
- web server
- Spring Security
- 도커
- 자바
- Today
- Total
개발 일기
[Java] Java는 Call by Value인가? Call by Reference인가? 본문
삼성 SW 역량테스트 2022 하반기 오전 1번 문제 싸움땅을 풀면서 아래와 같은 코드를 작성했다.
삼성 SW 역량테스트 2022 하반기 오전 1번 문제 싸움땅
import java.util.*;
import java.io.*;
/**
* - n*n 격자에서 진행
* - 각 격자에 무기
* - 초기에 무기들이 없는 빈 격자에 플레이어들이 위치하며 각각은 초기 능력치 가짐(모두 다름)
*
* 숫자
* - 빨간색 배경의 숫자 = 총의 공격력 / 플레이어의 초기 능력치
* - 노란색 배경의 숫자 = 플레이어의 번호
*
* Round 설명
* 아래 과정을 1번 부터 n번 플레이어까지 "순차적으로" 진행해야 한 라운드가 끝난다.
* 1-1. 첫 번째 번호 플레이어 부터 본인이 향하고 있는 방향대로 한칸 이동.(격자를 벗어나면 정 반대 방향으로 바꾸어서 한 칸 이동
* 2-1. 이동 후 해당 칸에 플레이어 없으면 총이 있는지 확인하고 총이 있으면 총을 획득(이미 가지고 있으면 공격력이 더 쎈 총을 획득하고 나머지 총은 다시 놓아둠.
* 2-2-1.만약 이동한 방향에 플레이어가 있었다면? 싸우게된다. 초기 능력치 + 총의 공격력 합을 비교해 더 크면 이김.(같으면 초기 능력치 높은 플레이어가 승리)
* 이때 이기면 각 플레이어의 초기 능력치와 가지고 있는 총의 공격력의 합의 차이만큼 포인트로 획득한다.
* 2-2-2.졌으면 들고 있던 총을 격자에 내려놓고 기존 방향대로 칸 이동. 이동하려는 칸에 다른 플레이어가 있거나 격자 밖이라면 오른쪽으로 90도 회전하다가 빈 칸 보이면 이동
* (literally 빈칸이 아니라 이동할 수 있는 칸)
* 만약 해당 칸에 총이 있으면,해당 플레이어는 가장 공격력이 높은 총을 획득하고 나머지 총들은 해당 격자에 내려 놓는다.
* 2-2-3.이긴 플레이어는 승리한 칸에 떨어져 있는 총들과 원래 들고 있던 총 중 가장 공격력이 높은 총 획득하고, 나머지 총들은 해당 격자에 내려 둔다.
*/
public class Main {
static int n, m, k;
static int[][] map; // 해당 칸의 총 갯수 저장
static int[] scores;
static List<Player> players = new ArrayList<>();
static HashMap<String, List<Integer>> guns = new HashMap<>();
static int[] dy = {-1,0,1,0};
static int[] dx = {0,1,0,-1};
public static void main(String[] args) throws IOException {
// 변수 초기
init();
// 라운드 진행
while (k-- > 0) {
// 순차적으로 플레이어 이동
for(int p = 0; p < m; p++) {
Player player = players.get(p);
moveTo(0, player);
if (map[player.y][player.x] == 0) { // 2-1. 해당 칸에 플레이어 없음
// 총 있는지 확인
String gunsKey = player.y + " " + player.x;
List<Integer> gunInfo = guns.getOrDefault(gunsKey, new ArrayList<>());
if (gunInfo.size() != 0) getGun(player, gunInfo); // 총 있으면 줍
// map 초기화
map[player.y][player.x] = player.idx;
}
else if (map[player.y][player.x] > 0) { // 2-2. 해당 칸에 플레이어 있음
// 승자 체크
int anotherPlayerIndex = map[player.y][player.x] - 1; // 이미 있던 플레이어 인덱스
Player anotherPlayer = players.get(anotherPlayerIndex); // 다른 플레이어
int currentPlayerPower = player.s + player.gun;
int anotherPlayerPower = anotherPlayer.s + anotherPlayer.gun;
int powerGap = currentPlayerPower - anotherPlayerPower;
if (powerGap > 0) fightingResult(player, anotherPlayer, powerGap); // 현재 플레이어가 더 강함
else if (powerGap < 0) fightingResult(anotherPlayer, player, powerGap); // 기존 플레이어가 더 강함
else { // 파워 같을 때
if (player.s > anotherPlayer.s) fightingResult(player, anotherPlayer, powerGap);
else fightingResult(anotherPlayer, player, powerGap);
}
}
}
}
for (int score:scores) System.out.print(score + " ");
}
public static void fightingResult(Player winner, Player loser, int powerGap) {
// 점수 획득
scores[winner.idx - 1] += Math.abs(powerGap);
// 1. 진 플레이어 이동
// 1-1. 총 버리기
int loserGun = loser.gun;
if (loserGun > 0) { // 총 있으
loser.gun = 0;
String gunsKey = loser.y + " " + loser.x;
List<Integer> gunInfo = guns.get(gunsKey);
gunInfo.add(loserGun);
guns.put(gunsKey, gunInfo);
}
// 1-2. 이동 시키기
moveTo(1, loser);
map[loser.y][loser.x] = loser.idx;
String loserGunsKey = loser.y + " " + loser.x;
List<Integer> loserGunInfo = guns.get(loserGunsKey);
if (loserGunInfo.size() > 0) getGun(loser, loserGunInfo);
// 2. 이긴 플레이어 총 체인지
map[winner.y][winner.x] = winner.idx;
String winnerGunsKey = winner.y + " " + winner.x;
List<Integer> winnerGunInfo = guns.get(winnerGunsKey);
if (winnerGunInfo.size() > 0) getGun(winner, winnerGunInfo);
}
public static void getGun(Player player, List<Integer> gunInfo) {
int playerGun = player.gun;
int gunInfoSize = gunInfo.size();
int maxIndex = -1;
int maxValue = -1;
for (int i = 0; i < gunInfoSize; i++) {
if (maxValue < gunInfo.get(i)) {
maxIndex = i;
maxValue = gunInfo.get(i);
}
}
if (playerGun == 0) { // 줍기
player.gun = maxValue;
gunInfo.remove(maxIndex);
}
else { // 교체
if (playerGun < maxValue) {
player.gun = maxValue;
gunInfo.set(maxIndex, playerGun);
}
}
}
public static void moveTo(int type, Player player) { // 0은 1-1, 1은 2-2-2
int cy = player.y;
int cx = player.x;
int ny = cy + dy[player.d];
int nx = cx + dx[player.d];
if (type == 0) {
if (!(0 <= ny && ny < n && 0 <= nx && nx < n)) { // 범위 벗어나
player.d = (player.d + 2) % 4; // 정반대 방향
}
player.y = cy + dy[player.d];
player.x = cx + dx[player.d];
map[cy][cx] = 0;
}
else if (type == 1) {
while(true) {
if (!(0 <= ny && ny < n && 0 <= nx && nx < n) || map[ny][nx] > 0) { // 범위 벗어나거나 플레이어 존
player.d = (player.d + 1) % 4;
ny = cy + dy[player.d];
nx = cx + dx[player.d];
continue;
}
break;
}
player.y = ny;
player.x = nx;
}
}
public static void init() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
n = Integer.parseInt(st.nextToken()); // 격자 크
m = Integer.parseInt(st.nextToken()); // 플레이어
k = Integer.parseInt(st.nextToken()); // 라운드 수
map = new int[n][n];
// HashMap에 총 기
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < n; j++) {
int gunPower = Integer.parseInt(st.nextToken());
if (gunPower > 0) {
String gunsKey = i + " " + j;
List<Integer> temp = new ArrayList<>();
temp.add(gunPower);
guns.put(gunsKey, temp);
}
else {
String gunsKey = i + " " + j;
List<Integer> temp = new ArrayList<>();
guns.put(gunsKey, temp);
}
}
}
// 플레이어 map에 기
for (int i = 1; i <= m; i++) {
st = new StringTokenizer(br.readLine());
int idx = i;
int y = Integer.parseInt(st.nextToken()) - 1;
int x = Integer.parseInt(st.nextToken()) - 1;
int d = Integer.parseInt(st.nextToken());
int s = Integer.parseInt(st.nextToken());
map[y][x] = idx;
players.add(new Player(idx,y,x,d,s));
}
scores = new int[m];
}
public static class Player {
int idx;
int y, x, d, s;
int gun;
public Player(int idx, int y, int x, int d, int s) {
this.idx = idx;
this.y = y;
this.x = x;
this.d = d;
this.s = s;
this.gun = 0;
}
@Override
public String toString() {
return "{idx: " + idx + ", y: "+y+", x: "+x+", d: "+d+", s: "+s+ ", gun: " + gun + "}";
}
}
public static void printArr(int[][] arr) {
for (int[]row:arr) System.out.println(Arrays.toString(row));
}
}
문제 요구사항이 꽤 길고 조건이 많아, 클래스를 이용해 플레이어 상태를 저장하고, List에서 해당 Player 객체를 꺼내 메서드에 넘겨주며 조작하는 식으로 구현했다.
이때 나는 자바가 Call by Reference 방식이라고 생각하고, Player 객체를 메서드에 넘겨 작업을 수행한 후 별도로 반환하지 않아도 내부 필드 값이 바뀔 것이라고 기대했다. 실제로도 그렇게 동작했다.
그런데 문득 궁금해져서 찾아보니, 자바는 “Call by Value” 방식이라는 설명이 나와서 순간 당황스러웠고, 이에 대해 정리해보게 되었다.
우선 이 둘을 비교해보자.
🟠 Call By Value와 Call By Reference
🔸 Call By Value란?
Call by Value는 값을 복사해서 함수로 전달하는 방식이다. 즉, 함수 내부에서는 복사된 값을 가지고 작업하기 때문에 원래 값에는 영향을 주지 않는다.
public void modify(int x) {
x = 10;
}
int num = 5;
modify(num);
System.out.println(num); // 출력: 5
위 코드를 보면 num의 값을 modify() 함수에 넘겼지만, 함수 내에서 변경한 값은 원래 변수 num에 영향을 주지 않는다.
값만 복사됐기 때문이다.
🔸 Call By Reference
Call by Reference는 값이 아니라 변수 자체(주소)를 함수에 전달하는 방식이다.
함수 내부에서 값을 바꾸면, 원래 변수도 함께 바뀐다.
// C++ 예제
void modify(int &x) {
x = 10;
}
int num = 5;
modify(num);
cout << num; // 출력: 10
🔸 그럼 Java는?
Java는 철저히 Call by Value만 존재한다. 그런데 참조형 객체를 넘기면 왜 값이 바뀌는 것처럼 보일까?
그 이유는 다음과 같다.
- 자바에서는 객체 자체를 넘기는 게 아니라, 그 객체를 참조하고 있는 ‘참조값(주소)’을 복사해서 넘긴다.
- C 와 달리 자바에서는 포인터를 철저하게 숨겨 개발자가 직접 메모리 주소에 접근 하지 못하게 조치
- 이 복사된 참조값으로 객체의 내부 필드를 바꾸는 것은 가능하다. 하지만 참조 자체를 바꿔도 원래 참조에는 영향을 주지 않는다.
- 원시값을 복사하느냐, 주소값은 복사하느냐에 따라 반환 결과가 달라지기 때문에 이 둘을 구분하기 위해 call by value / call by address 로 명명 지어 구분하기도 한다.
class Dog {
String name;
Dog(String name) { this.name = name; }
}
void changeDog(Dog dog) {
dog.name = "Max"; // ✅ 객체 내부는 바뀜
dog = new Dog("Fido"); // ❌ 참조 자체를 바꿔도 원본엔 영향 없음
}
Dog myDog = new Dog("Buddy");
changeDog(myDog);
System.out.println(myDog.name); // 결과: "Max"
→ 즉, 자바는 모든 인자를 값으로 전달(Call by Value)하지만, 객체를 넘길 경우 '객체의 참조값(주소)'이 복사되기 때문에 객체 내부는 변경될 수 있다.
'Back-End > Java' 카테고리의 다른 글
[Java] GC - Garbage Collector (0) | 2025.04.15 |
---|---|
[Java] SOLID 원칙 (0) | 2025.04.14 |
[Java] JAVA 코드가 실행되기까지... (Java = Interpreter + Compiler) (1) | 2025.03.27 |
[Samsung SW 역량 테스트 대비] 좌표 변환(회전, 대칭 등) (0) | 2025.03.01 |
[Algorithm] Segment Tree(세그먼트 트리) (0) | 2025.02.07 |