Java 는 “메모리 전체를 하나의 거대한 배열"로 다루는 언어에서는 누릴 수 없는 장점을 갖고 있습니다.
사실 보안적으로는 Java 에서 어떤 일이 일어날 수 있는지에 대해서는 정확히 모르겠습니다만, C, C++ 의 경우는, 메모리를 바이트 배열로 바라보며, 포인터를 통한 접근을 하기 때문에 “권한이 없더라도 즉 할당하지 않은 메모리" 에 대한 접근을 하는 것이 허용되기도 합니다.
Java 에서는 JVM 을 통해 바이트코드를 실행하기 때문에, 할당된 메모리 외부에 대하여 읽고 쓰는 코드에 대한 기본적인 검사를 수행해 줍니다.( 더 자세한 내용은 개인적으로 찾아보면 좋을 것 같슴다.. )
하지만 Java 역시 적절하게 코드가 작성되어있지 않으면 “ 예상하지 못했다고 생각되는 접근, 변경" 이 발생할 것입니다.
적절하지 않은 클라이언트로부터, 클래스를 보호 해야합니다
- 악의의 사용자 또한 존재하겠으나 대부분의 경우는 개발자에 의해 자기도 모르게 , 객체의 내부값을 수정하는 경우들 역시 매우 빈번합니다.
다음 예시를 살펴봅시다.
public final class Period {
private final Date start;
private final Date end;
public Date start() {
return this.start;
}
public Date end() {
return this.end;
}
public String toString() {
return start + " - " + end;
}
}
위와 같이 인스턴스 필드 선언에 final 키워드를 사용하더라도, Date 라는 “가변 객체" 를 사용할 경우 Period 클래스는 불변이 아니다.
가변객체를 사용하며 아래와 같은 상황이 발생한다
현재 스레드에서 생성한 Period 객체를, 커스텀하게 생성한 스레드 풀의 스레드들이 받아서 사용하도록 하였다.
그 결과, Period 의 start 인 Date 의 값이 변경됨을 확인할 수 있다. ——> 불변이 아니라는 것 💥
@Nested
@DisplayName("동시성 테스트")
public class ConcurrentTest {
private long timeToAdd = 1000L;
@Test
@DisplayName("동시에 실행 중인 여러 스레드에서 하나의 Period 를 공유할 경우 예상하지 못한 결과가 발생한다")
void testFixedThreadPool() throws Exception {
long startTime = 1000L;
Period period = new Period(new Date(startTime), new Date(1500));
Date startDate = period.start();
runConcurrentTasks(period);
Assertions.assertThat(period.start())
.isEqualTo(startDate);
Assertions.assertThat(period.start().getTime())
.isNotEqualTo(startTime); // 현재 스레드에선 startTime 을 변경한적 없음에도 startTime이 달라졌다
}
}
Date 를 대체하는 **불변 객체인 LocalDateTime, ZonedDateTime, Instant**
가 새롭게 등장하였으니 이들을 사용하는 것으로 변경할 수 있다.