1주차 강의는 Java에 대한 기초적인 내용이었다.
강의 내용을 그대로 학습일지로 작성하기보다 강의를 들으면서 헷갈렸던 부분들과 떠오른 의문들을 확장해나가는 방향으로 적어나갔다.
강의를 듣다가 new라는 키워드를 사용할 때, 자바에서 Heap 메모리에 객체가 할당된다는 것을 알게 되었다.
운영체제 과목에서 Stack과 Heap에 대해서 배우고 있어서 이것과 관련해서 추가로 공부해보았다.
자바에서는 어떻게 Stack과 Heap 메모리에 할당이 이뤄질까?
이를 이해하기 위해서는 먼저 JVM(자바 가상 머신)이 운영체제로부터 할당받는 메모리 영역에 대해서 이해할 필요가 있다.
JVM은 크게 Method, Stack, Heap 메모리 영역으로 구분해서 메모리를 할당한다.
Method 영역의 경우 클래스 정보, 메서드, 클래스 변수(정적 변수), 상수(Final) 등이 저장된다.
Heap 메모리 영역의 경우 프로그램이 실행 도중에 동적으로 할당 받는 공간으로, new 연산자로 객체를 생성할 때마다 객체가 저장된다.
Stack 메모리 영역의 경우 메서드의 지역변수, 메서드 호출 정보, 스택 프레임(각 메서드 호출마다 생성되는 영역) 등이 저장된다.
public class StudentTest{
public static void main(String[] args) {
int old = 20;
// old는 지역 변수 -> stack에 할당
// 10은 기본 자료형 타입 -> stack에 할당
Student student = new Student();
// student는 지역 변수 -> Stack에 할당
// new Student() -> Heap에 동적 할당
}
String s = "Kang"
// s는 지역 변수 -> stack에 할당
// "Kang"은 String Object -> Heap에 할당
}
운영체제 과목에서 배웠던 메모리 영역과 JVM에 의해 나뉘어지는 메모리 영역이 조금 달라서 헷갈렸다.
JVM에 대한 추가적인 학습이 필요해 보인다.
접근 제어자는 4가지가 있다.
public class MakeReport {
StringBuffer buffer = new StringBuffer();
private String name = "이름";
private String address = "주소";
private String phoneNum = "010-1234-5678";
private void makeName()
{
buffer.append(name);
}
private void makeAddress()
{
buffer.append(address);
}
private void makePhoneNum()
{
buffer.append(phoneNum);
}
public String getReport()
{
makeName();
makeAddress();
makePhoneNum();
return buffer.toString();
}
}
public class TestReprt {
public static void main(String[] args) {
MakeReport report = new MakeReport();
String builder = report.getReport();
System.out.println(builder);
}
}
public class Book{
String title
public void SetTitle(String name){
this.title = name;
// 여기서 this는 자신의 메모리(자신의 주소값 반환),
// 즉 인스턴스 자신을 가리킴
}
}
public class Person {
String name;
int age;
public Person() {
// 이렇게 다른 생성자를 호출할 때, 그 이전 statement 사용x
this("이름없음", 1);
// 생성자가 여러개인 경우, this를 사용하여 생성자에서 다른 생성자 호출 가능
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
프로그램에서 인스턴스가 단 한개만 생성되어야 하는 경우 사용하는 디자인 패턴이다.
public class Company{
private static Company instance = new Company();
private Company(){}
public Comany getInstance(){
if (instance == null){
instance = new Company();
}
return instance;
}
}
내 기억 속에 있는 Singleton 패턴과는 달라서 추가로 검색해보았다.
그 결과 위와 같은 Singleton 패턴은 Eager Initialization으로 객체를 사용하지 않더라도 무조건 객체를 생성하기 때문에 자원낭비
가 발생할 수 있다는 단점이 있다는 것을 알게 되었다.
현재 가장 널리 쓰이는 Singleton 패턴은 Inner Static Helper Class를 사용하는 구현 방식이다.
public class Singleton{
private Singleton(){}
public static class SingletonHelper{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonHelper.INSATNCE;
}
}
이와 같은 lazy-loading 방식으로 자원의 낭비를 피할 수 있다.
물론 이 방식도 Java의 Reflection
을 사용하면 private 생성자, 메서드에 접근이 가능해져서 단 하나의 객체라는 조건을 깨뜨릴 수도 있다는 단점이 있다.
public class CarFactoryTest {
public static void main(String[] args) {
// 아래와 같은 코드가 작동하도록 클래스를 작성하시오
CarFactory factory = CarFactory.getInstance();
Car mySonata = factory.createCar();
Car yourSonata = factory.createCar();
System.out.println(mySonata.getCarNum()); // 10001
System.out.println(yourSonata.getCarNum()); // 10002
}
}
나는 아래와 같이 작성하였다.
public class CarFactory {
private int num = 10000;
private CarFactory(){};
private static CarFactory INSTANCE = new CarFactory();
public static CarFactory getInstance(){
return INSTANCE;
}
public Car createCar() {
return new Car(++num);
}
}
public class Car {
int num;
public Car(int num){
this.num = num;
}
public int getCarNum(){
return num;
}
}