안녕하세요.
지금까지 계속 저만의 기술 블로그를 만들어야지, 만들거야
마음으로만 다짐하다가 인제야 시작하게 되었습니다.
비록 시작은 코딩일기지만, 그 끝은 창대하게
어엿한 개발자 블로그로 성장할 수 있도록 노력하겠습니다.
해당 게시글은 백기선님의 Java live-study 의 과제 내용을 정리한 글입니다.
자바의 변수 형태는 크게 Primitive Type과 Reference Type으로 구분된다.
Primitive Type
기본값 이라는게 존재하지 않기 때문에 null이 존재하지 않는다.
실제 값을 변수에 저장하여 사용하는 형태이다.
이 값은 메모리 영역중 Stack 영역에 저장된다.
Reference Type
Primitive Type을 제외한 타입은 전부 Reference Type이다.
Primitive Type과 달리 null이 존재한다.
객체가 저장되어 있는 곳의 메모리 주소값(참조값)을 저장하는 변수이다
이 변수는 메모리 영역중 Heap 영역에 저장된다.
Primitive Type과 Reference Type의 변수가 어떻게 할당되는지 그림으로 살펴보자.
public class Main
{
public static void main(String[] args)
{
int number;
Car car;
}
}
Primitive Type의 예시로 int 타입의 변수 number 와
Reference Type의 예시로 Car 타입의 변수 car를 생성해보았다.
그러면 다음과 같에 메모리에 두 변수가 자리잡을 것이다. C와는 다르게 Java에서는 이 메모리값을 컴파일러가 알아서 다 처리해주기 때문에 알 필요 없다.
현재 두 변수는 값이 들어가 있지 않으므로 다음과 같이 값을 넣고 변화를 보자.
public class Main
{
public static void main(String[] args)
{
int number;
Car car;
number = 3;
car = new Car();
}
}
다음과 같이 number 변수에는 실제 값 3이 들어가게 되고, car 변수에는 car 실제 객체의 주소값이 들어가게 된다(참조한다). 상기에 서술했듯 C와는 다르게 이 주소값을 알 필요는 없다. 어딘가에 저장이 되어있고 이는 컴파일러가 알아서 관리해주기 때문이다.
추가로 Primitive Type과 Reference Type은 null 값의 유뮤가 차이점이었는데
Reference Type에 null값을 넣었을 때도 살펴보자.
car 변수에 아무 객체도 넣지 않거나, null을 명시적으로 적어주면 된다.
public class Main
{
public static void main(String[] args)
{
Car car;
car = null;
Car car = null;
// 셋다 null값이 들어간다.
}
}
그러면 메모리상에서는 이렇게 된다.
Reference Type에서 null값이 존재하는 이유는 Garbage Collector(GC)
가 더이상 쓰이지 않는 (null값이 들어가서 객체와의 링크가 전부 끊어져 있는) 변수들을 주기적으로 찾아 메모리를 해제하기 때문입니다.
추가로 Wrapper Class 라는 것이 있다. 이는 int와 같은 Primitive Type의 변수가 null값을 허용해야 하는 경우 등의 필요에 의해서 Reference Type로 취급하기 위해 존재한다. Wrapper class 는 다음과 같다.
그림 출처
Primitive Type 을 Wrapper Class 로 감싸는(Wrap) 행위를 박싱 Boxing 이라고 하고 반대로 Wrap되어 있는 변수를 Primitive Type으로 변환하는 행위를 언박싱 Unboxing 이라고 한다.
리터럴, literal 뜻 그대로 "그 자체" 라는 의미입니다.
int number = 3;
에서 number는 변수이고 이 "3"이라는 값이 리터럴이다.
상수와 혼동하는 경우가 있는데 상수는 변하지 않는 변수 즉, 변수이다.
시작하기 앞서 변수의 3가지 종류에 대해서 먼저 알아보자.
클래스 변수 : 같은 클래스에서 만들어진 모든 인스턴스가 공유하는 변수이다.
public class Car
{
static int price = 1000;
public static void main(String[] args)
{
Car car1 = new Car();
Car car2 = new Car();
System.out.println(car1.price);
car2.price = 1500;
System.out.println(car2.price);
System.out.println(car1.price);
}
}
출력 1000
1500
1500
price는 클래스 변수이기 때문에 car2 인스턴스를 통해 변경하게 되었을때 car1의 price도 변경된다.
인스턴스 변수 : 클래스가 아닌 인스턴스를 통해서만 접근이 가능하며 각 인스터스마다 다른 고유한 장소에 저장된다.
public class Car
{
int price = 1000;
public static void main(String[] args)
{
// System.out.println(price);
Car car1 = new Car();
Car car2 = new Car();
System.out.println(car1.price);
car2.price = 1500;
System.out.println(car2.price);
System.out.println(car1.price);
}
}
출력 1000
1500
1000
이번에는 price를 인스턴스 변수로 바꿨을때 car2를 통한 price의 변경이 car1의 price에는 영향을 미치지 않는 것을 확인할 수 있다. 참고로 인스턴스 변수는 인스턴스를 통해서만 접근이 가능하기에System.out.println(price);
같은 접근은 에러가 발생된다.
지역 변수 : 메소드 내에 선언되어 해당 메소드 내에서만 사용이 가능한 변수이다.
변수 초기화
지역변수는 사용하기 전에 필수로 초기화를 해줘야 한다. int number = 3;
멤버 변수의 초기화에는 3가지 방식이 있다.
class Car
{
int price = 1000; // primitive type
Model model = new Model(); // reference type
}
class TestBlock
{
static { } // 클래스 초기화 블록
{ } // 인스턴스 초기화 블록
}
클래스 초기화 블럭 : "클래스 변수" 초기화에 사용되며 클래스가 메모리에 처음 로딩될 때 한번만 수행
인스턴스 초기화 블럭 : "인스턴스 변수" 초기화에 사용되며 생성자와 같이 인스턴스를 생성할 때 마다 수행
(인스턴스 변수의 초기화는 주로 생성자를 쓰기 때문에 잘 사용하지 않음)
(생성자 보다 인스턴스 초기화 블럭이 먼저 수행)
클래스가 처음 로딩될 때 클래스 변수들이 자동적으로 메모리에 만들어지고나서
바로 클래스 초기화 블럭이 클래스 변수들을 초기화한다.
상기에 서술한 변수의 종류를 다시 살펴보자.
public class Car
{
static int tmpValue; // 클래스 변수
int price = 1000; // 인스턴스 변수
public static void main(String[] args)
{
int tmp = 3; // 지역변수
}
}
각 변수의 종류별로 Scope와 Life Cycle을 표로 정리해보았다.
초기화 되는 순서는
클래스 초기화 블록 -> main 메소드 -> 인스턴스 생성 -> 인스턴스 초기화 블록 -> 생성자
이다.
타입 변환 : 서로 타입이 다른 변수간의 연산을 수행할때 타입을 맞춰주어야 하는데 이때 타입을 변환 시키는 것을 Type Casting 형변환 이라고 한다.
캐스팅 : 형변환에는 명시적으로 형변환을 하는 경우(캐스팅)와 자동으로 형변환이 되는 두 가지 경우(프로모션) 가 있다.
캐스팅(명시적 형변환)
double dNumber = 123.456;
int iNumber = (int)dNumber; // 형변환
System.out.println(iNumber); // 123 출력
위와 같이 (변환 할 type)변환 할 변수명
으로 명시적으로 형변환이 가능하다.
프로모션(묵시적 형변환, 자동 형변환)
캐스팅을 별도로 하지 않아도 필요에 의해서 자동으로 형변환이 되는 경우도 있다. 하지만 이때에는 규칙이 존재하는데 프로모션으로 인한 값손실이 발생하면 안되기 때문에 값 표현이 좁은 타입에서 넓은 타입으로의 프로모션만 가능하다.
(1)byte < (2)short < (4)int < (8)long < (4)float < (8)double
Java에서 배열을 선언하고 생성하는 방식은 다음과 같다.
int[] arr = new int[배열의 크기];
int[][] arr = new int[행의 크기][열의 크기];
다음과 같이 배열을 생성하고 초기화 할 수 있다.
int[] arr = new int[3]{1, 2, 3};
int[][] arr = new int[3][3]{{1, 2, 3},
{4, 5, 6},
{7, 8, 9}};
배열의 크기를 다음과 같이 입력받아 동적으로 생성할 수 있다.
int arrSize = 입력받기;
int[] arr = new int[arrSize];
int row = 입력받기;
int col = 입력받기;
int[][] arr = new int[row][col];
타입 추론이란, 변수를 생성할때 명시적으로 Data Type을 선언하지 않았지만 컴파일러가 해당 변수의 타입을 추론하여 타입을 정하는 것이다.
타입 추론이 쓰이는 곳이 Generic, Lambda, Var 가 있는데 이중 var 키워드가 Java 10 부터 사용가능하다.
var 키워드로 변수를 생성할 시에는 반드시 선언과 동시에 초기화를 해주어야한다.
var number = 100; //int 타입
var text = "Hello, World"; // String 타입
var는 사실 이름만 들어보았는데 개인적으로 코드를 보기 어렵게 만드는 것 같아 그 동안 쓰지 않아왔고 따로 찾아본적도 없었다. 이번 기회에 찾아보고 알게되었는데 솔직히 앞으로 이 키워드를 쓸지는 잘 모르겠다..
var 외에도 Generic과 Lambda에 대해서 예전에 보다가 이해가 너무 안가서 도중에 덮은적이 있는데 .. 이번 과제를 시작으로 다시 한번 학습해야겠다 ..
(모르는게 많아도 너무 많다 ㅠㅠ)
변수에 관한 대부분의 내용들은 사실 Java언어를 처음 학습할때 예시를 따라치면서 발생하는 에러들을 검색해보면서, 부딪혀가면서 아는 것이 전부였다. 그래서 실제로 쓸때 필요한 만큼만 알고 있었다는 것임을 이번 과제를 통해 느끼게 되었다. 변수에 관한 기본적인 내용들을 깊이 이해하고 있다면 난해한 에러에도 유려하게 대처할 수 있을 것 같다.
https://gbsb.tistory.com/6
https://mommoo.tistory.com/14
https://ramees.tistory.com/16
https://whatisthenext.tistory.com/30
https://velog.io/@bk_log/Java-%ED%83%80%EC%9E%85-%EC%B6%94%EB%A1%A0