본문 바로가기
{Java}

21.07.18{코딩일기} 컴퓨터가 참이라고 확신하게 만드는 근거조작방법

by Davey 2021. 7. 19.
728x90
도대체 컴퓨터는 무슨근거로
명제를 참이라고 판단하는건가?


도대체 모가그렇게 불만인
우리 사랑스러운 java엔진은 내가 정성스럽게 만든
명제를 참이라고 못읽구
내가 입력한 데이터를 else처리 해버리는거뉘?
java왈 : 사람이문제다.

ㅋㅋㅋ결론부터 말하면 String타입을 선언하고 같은 값을 부여했더라도 서로의 주소값이 달라서 벌어진 헤프닝이었다.

주소는 뭐고 스트링 타입선언은 뭐냐?


#스트링타입 이란?
우선 스트링 타입은
String you = 'true'; 요로콤 you라는 변수를 String타입으로 선언하는 걸 말하고,
주소는 뭐냐?
이건 String변수를 생성할때 주소를 메모리에 할당하는 방법 2가지를 설명해야한다.

지금 여러분 앞에 냄비가 여러개가 쌓여있다고 상상해보자.
엄마가 푸짐한 저녁한끼를 차려주셨는데 육개장이랑 설렁탕이 남아서 이걸
위 그릇에 담아 보관하려고 가정해보자. 여러분은 설렁탕과 육개장을
하나의 냄비에 다 들이부어 담아 보관할것인가??

뭐 개인취향일 수 있는데, 여기서 필수전제조건은
국을 보관할땐
똑같은 국물이라도
종류별로 따로 담아
보관해야한다고 가정해보자.
절대 지금배고파서 이런예시드는거다...(ㅋㅋㅋㅋㅋㅋㅋ)

자 그럼,
여기서 파란냄비에는 육개장을 담고 빨간냄비에는 설렁탕을 담아 냉장고에
보관한다면 뭐가 좋을까?
우리는 육개장이 먹고 싶으면
오롯이 육개장만 꺼내어 먹고
설렁탕을 먹고 싶으면
오롯이 오리쥐냘 퓨어 설렁탕만
즐길 수 있게 된다.
혼합잡탕이 안된다는 말이다 ㅋㅋㅋㅋ(띠용?)

즉 String a, String b가 육개장, 설렁탕이라면
파란냄비가 육개장 전용 주소이고,
빨간냄비가 설렁탕 전용 주소가 된다는 맥락으로 이해하면 되겠다.

위와동일한 방식으로 ,
java에서는 똑같은 String변수를 생성하더라도 변수 고유값을 지키기 위한
방법으로 주소할당을 달리한다.

#java에서 String 변수 생성방법 2type
첫째는 리터럴을 이용한 방식이다.
리터럴(literal)이뭐냐고?
명사로는 '오탈자'라는 뜻이고 형용사로는 '(은유 비유없이 문맥그대로)정확한', '융통성없는'이란 뜻이다.
이게 컴퓨터용어로는 소스코드의 고정된값을 의미하는데

도대체 뭐가 고정된건가??
변수에 넣는 변하지 않는 데이터를 의미한다.
예를들면
int a = 1; 에서 1이 리터럴(literal)에 해당하는거다.
boolean, char, double, long, int..등의 데이터타입이 이에 해당한다.

근데,
String a = "";이건 뭐지?
애도 변하지 않는 데이터로 만들 수 있지 않나?
애는 사실 예외케이스이다.
왜?
String은 클래스타입 변수인데

일반적으로 class타입 변수는 동적으로 변하는 값을 가져야한다.
왜냐하면 값이 언제 바뀔지 모르기 때문이다.

String만 좀 예외 케이스인거다.
객체리터럴이라고 들어보았는가?
객체리터럴이 바로 String처럼 한번생성하면 객체안의 데이터가
변경되지 않는 클래스를 일컫는다.

이와는 반대로 상수가 있다.
#상수란(Constant)?
상수는 변하지 않는 변수를 의미하며(메모리 위치) 메모리 값을 변경할 수 없다.
상수는 숫자만 넣어야 한다고 오해하는 사람들이 많은데,
상수는 변하지 않는 변수를 뜻하는 것이므로,
즉 상수에 넣는 데이터는 숫자뿐만아니라
String클래스나 Color 구조체 같이 기본형에서 파생된 객체나 유도형같은 데이터를 넣을 수 있다.

프로그래밍에서 상수를 쓸때는 C,C++,C#은 const , Java는 final 제어자를 써야하는데,
Java언어로 예를 들어보자.

즉 Test라는 클래스를 만들었다면,

final Test t1 = new Test(); // Test클래스 메모리생성
t1 = new Test(); // 또 생성? 노노노안돼

는 불가 하지만,

t1.num = 10;

이렇게 클래스 안의 데이터를 변경해도 상관이 없다는 의미이다.

리터럴은 변수의 값이 변하지 않는 데이터(메모리 위치안의 값)를 의미한다.

보통은 기본형의 데이터를 의미하지만, 특정 객체(Immutable class , VO class)에 한에서는 리터럴이 될 수 있다.

# java는 메모리생성과 선언과정이 별개
java는 클래스변수를 생성할때
선언과 메모리생성과정이 구분되어 있다.
그래서 아래 코드처럼 우리가 java 코드를 짜야되었던 것이다.
여기서 Food는 클래스명이다.

1
2
Food chocolate; //변수의 선언
chocolate = new Food(); //메모리의 생성

클래스명앞에 제어자는 생략했는데 보통, java에서 변수,함수,클래스명을 선언할때 제어자를 사용한다.

어디서 많이 본것들 아닌가?


- public : 접근에 제한이 없음
- protected : 동일한 패키지 내에 있거나, 파생 클래스의 경우 접근 가능
- default : 아무런 접근제어자를 명시하지 않았을 경우 할당되는 제어자이며, 동일한 패키지 내에서만 접근 가능
- private : 자기 자신의 클래스 내에서만 접근 가능

private -> default -> protected -> public 순으로 보다 많은 접근을 허용하는점 참고바란다.

아 String 타입은 이제 알겠고,
그래서 내가 입력한 String변수값이
왜 if조건문하고 불일치하는지
이유를 알려달라구..!!

앞서
String타입을 선언하고 같은 값을 부여했더라도 서로의 주소값이 달라서 벌어진 헤프닝
이라고 이야기 했던걸 곱씹어보자.
String enoughTime = ""라고 정의한 enoughTime라는 변수는 객체리터럴값이 존재하는가?에 대한 질문에
이젠 여러분이 답할 수 있을것이다.

정답은 null값이 존재한다.
enoughTime이라는 변하지 않는 리터럴값이 클래스변수타입인 Striag에 존재한다는 의미이다.

그럼 애가 저장되는 메모리는 어디에 생성되는가?
이미 따로 생성되어 있는데 그게 바로 string constant pool에 저장된 String 객체의 주소값에 저장되는것이다.

일반 객체처럼, 즉 일반 다른 클래스 변수들은 Heap 영역에 생성된 String 객체의 주소값에 저장되는거와 달리,
객체리터럴 클래스 변수인 yes는 string constant(오....우리 constant 아까 위에서 배웠죠? 주소값 즉 메모리값이 변할 수없는거 기억하쥬?) pool의 String 객체 주소값에 저장된다.

근데 ㅋㅋㅋㅋㅋ내문제는 enoughtTime과 비교할 비교대상값이 없었던 문제였다.
메모리만 생성하고, 변수를 선언안한거였다(바보..) ㅋㅋㅋㅋㅋ

그니까 이게 무슨 소리냐면 너가 사용자 입력값을 enoughTime에다가 넣는걸 어떻게 했는데?
Scanner input_some = new Scanner(System.in);
String enoughTime = input_some.next();
// next에서는 사용자가 입력한 값을 토큰이라고 하는데 이 토큰을 문자열로 리턴해주고 그 리턴값을 enoughTime에 담은거잖아.

너가 만든 조건문을 보면
if (enoughTime == yes) {
DoAction();
} elseif ( enoughTime == no) {
DoNotAction();
} else {
DontDoThat();
}

java조건문에서 '=='이라는건 두객체의 리터럴값을 비교하는게 아니라 객체의id값만 비교하는거거든?
id값이 주소값인거잖아?
근데 잘보면 ㅋㅋㅋㅋㅋ yes랑 no는 내가 선언한적이 전혀 없는 객체들인거야.. ㅋㅋㅋㅋㅋ
그러니깐 컴퓨터에서는 yes라는 객체도 없고 객체리터럴값도 없고 id값도 없고 하니깐
당연히 else처리하고 DontDoThat();을 실행시킬 수 밖에 없는 필연적인 구조인거다..
결국 사람이 잘못했지 항상 컴퓨터는 잘못없다...으으으응....

다시 복기해보면 서로의 주소값이 다르다?는게 뭐냐?
객체id값이 달라서 컴퓨터가 두 비교대상의 id값이 일치하지 않는다고 판단하고 false로 해당조건절을
skip해버린다는건데.

#나같은경우에는 문제가 뭐냐? 비교대상을 일차적으론 잘못 선정했고, 또 선언하지도 않은 객체를 조건문안에 비교대상자로 넣어 넣고 있었다.

#그러면 해결책이 뭐야?비교대상객체를 똑같은 객체리터럴 변수로 선언해주고, 어차피는 모든 객체리터럴 메모리 생성은 동일하게 한곳!앞서 위에서 말했던 이미 빌트인되어있는 string constant pool의 String객체 주소값(이게 결국 하나의 id값인거야)에 저장되므로 따로 메모리생성해줄 필요 없지.

그래서 코드가 결국 아래와 같이 되는거다.


#변수선언

2번 '지각'예시만 봐라 비교객체리터럴값 2개를 추가로 생성해주었다.
여기서 이제 equals(e)라는 String 클래스안에 들어있는 메소드를 사용해 두 비교대상의 id즉 주소값이 아닌 리터럴값만 비교값만 비교해준다.

이전에 "두객체의 리터럴값을 비교하는게 아니라 객체의id값만 비교하는거거든? " 라고 앞서 위에서 언급했던걸 떠올려보면 이마를 타닥!하고 짚고 치게 될것이다... 굳이 equals메소드가 왜 필요하고 쓰이는지 설명하지 않겠다.

Copyright ⓒ 2021 by bluevulpe All Contents cannot be copied without permission.

728x90

댓글