| 백기선님의 라이브 스터디를 참고하여 작성한 게시물입니다.
자바는 statically-typed
언어다. 즉, 모든 변수들은 사용되기 전에 선언되어야 한다.
선언은 변수의 타입과 이름을 명명하는 작업을 포함한다.
int gear = 1;
이렇게 선언하는 것은 프로그램에게 초기값이 1인 Integar literal을 보관하는 gear라는 이름의 int 타입의 필드가 존재한다
는 것을 알려주는 것이다.
변수의 데이터 타입은 그 변수가 보관하는 값과, 그 값이 수행할 수 있는 operation에 따라 결정된다.
primitive type
은 자바가 예약어(reserved keyword)로 미리 결정한 타입들을 의미한다. Primitive value
들은 다른 Primitive value들과 상태를 공유하지 않는다.
primitive type의 변수를 선언할 때는 new
키워드를 사용하지 않는다.
primitive type은 class로 생성된 객체가 아니다. -> primitive type들은 언어에 내장된 특별한 데이터 타입
Java가 데이터를 다루는 최소 범위는 1 Byte이다. 고로 primitive type 변수들은 1Byte로 나누어 떨어지는 크기를 갖는다.
자바는 8개의 primitive type
을 지원한다.
2의 보수를 사용하는 8bit의 데이터 타입 -> -128 ~ 127의 값 & 1byte 크기
0
을 초기값으로 가짐
2의 보수를 사용하는 16bit의 데이터 타입 -> -32,768 ~ 32,767의 값 & 2byte 크기
0
을 초기값으로 가짐
2의 보수를 사용하는 32bit의 데이터 타입 -> -2^31 ~ 2^31-1의 값 & 4byte 크기
0
을 초기값으로 가짐
2의 보수를 사용하는 64bit의 데이터 타입 -> -2^63 ~ 2^63-1의 값 & 8byte 크기
0L
을 초기값으로 가짐
single-precision 32-bit IEEE 754 floating point
을 구현한 데이터 타입 -> 오차 발생 가능
4byte 크기
정확한 연산이 중요한 경우에는 java.math.BigDecimal
class를 사용
0.0f
을 초기값으로 가짐
double-precision 64-bit IEEE 754 floating point
을 구현한 데이터 타입. -> 오차 발생 가능
8byte 크기
0.0d
을 초기값으로 가짐
true, false 두 가지 값만 가질 수 있는 데이터 타입 -> 1bit 정보 저장용
1byte 크기
(1bit 정보 저장 != 데이터 타입 사이즈가 1bit인건 아님)
false
를 초기값으로 가짐
유니코드를 저장하는 16bit의 데이터 타입(0 ~ 2 ^ 16)
2byte 크기
\u0000
을 초기값으로 가짐
선언되었으나 값이 할당되지 않은 primitive type의 변수들은 컴파일러에 의해 데이터 타입에 따라 0, null, false 등의 값을 기본값을 가진다.
이러한 기본값에 의존해 코드를 작성하는 건 좋지 않다.
지역 변수들은 compiler가 초기화(기본값으로 할당)하지 않는다.
- 초기화되지 않은 변수에 접근하면 compile-time error가 발생한다.
JVM의 피연산자 스택이 피연산자를 4 Byte 단위로 저장 하기 때문에 int보다 작은 자료형의 값을 계산시 int 형으로 형변환 되서 연산이 수행된다.
스트링 타입은 참조형에 해당하지만 new
키워드 없이 사용할 수 있다
String myString = "myString";
String yourString = new String("yourString");
literal
은 고정된 값을 표현하는 소스 코드 -> 별도의 연산 작업 없이 바로 코드를 통해 들어남
literal
을 primitive type의 변수에 할당할 수 있음
boolean result = true;
char capitalC = 'C';
byte b = 100;
short s = 10000;
int i = 100000;
integer literal
은 끝부분에 L or l
이 붙어있으면 long
타입, 그렇지 않으면 int
타입으로 결정된다.
l말고 L붙이기를 권장한다 ('1'과 구분이 잘된다)
byte, short, int 숫자형 타입들의 값은 int
literal로 생성된다.
int형의 범위를 벗어난 long 타입은 long
literal로 생성된다.
Integer literals들은 Decimal
, Hexadecimal
, Binary
를 표현할 수 있다.
Java SE 7이후 부터는 Binary 전용 Int Literal을 사용할 수 있다.
Java SE 7이후 부터는 _(underscore)
를 사용해서 Integer literal의 가독성을 높일 수 있다
underscore는 digit 사이에만 위치할 수 있다
// The number 26, in decimal
int decVal = 26;
// The number 26, in hexadecimal
int hexVal = 0x1a;
// The number 26, in binary
int binVal = 0b11010;
// _를 사용해 가독성 높이기
long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi = 3.14_15F;
long hexBytes = 0xFF_EC_DE_5E;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;
float, double 타입에 사용되는 리터럴
F or f
로 끝나면 float 타입, 아무것도 없거나 D or d
로 끝나면 double 타입으로 결정된다.
E or e
을 사용하여 scientific notation을 표현할 수 있다.
double d1 = 123.4;
// same value as d1, but in scientific notation
double d2 = 1.234e2;
float f1 = 123.4f;
UTF-16 Unicode 인코딩 방식을 사용한다. 그러므로 char
, String Literal
은 Unicode (UTF-16)를 포함한다.
에디터, 파일 시스템이 지원하면, 코드에 직접 유니코드를 사용할 수 있는데, 불가능한 경우에는 직접 "Unicode escape(EX: \u0108)"을 사용해야 한다.
char literal
은 'single quotes', String literal
은 "double quotes"로 표현해야 한다.
char literal은 빈 칸을 사용할 수 없다
char myChar = ''; // compile error
String myString = ""; // no error
자바는 몇 가지 특별한 escape sequence를 제공함
\b (backspace)
\t (tab)
\n (line feed)
\f (form feed)
\r (carriage return)
\" (double quote)
\' (single quote)
\\ (backslash)
모든 종류의 reference type변수의 값으로 사용할 수 있는 리터럴 -> primitive type에는 사용 불가능
Finally, there's also a special kind of literal called a class literal, formed by taking a type name and appending ".class"; for example, String.class. This refers to the object (of type Class) that represents the type itself.
리터럴과 변수의 타입이 다른 경우, 명시적(Type Casting) 또는 묵시적(Type Promotion)으로 타입을 맞춰줄 수 있다.
변환하려는 변수의 타입에 따라 값 손실이 발생할 수 있다.
int i = 12;
double d = (double) i; // 작은 사이즈 -> 큰 사이즈 = 값 손실 X
double d = i; // float 타입 변수에 integer literal을 대입 -> integer literal 뒤에 f 없음 => 기본값으로 double로 인식 ==> (type) 생략 가능
double d = 12.3; // double은 8byte
int i = (int) d; // int는 4byte -> 값 손실 발생 => i == 12
특정 상황에서 컴파일러를 통해 묵시적 형변환이 발생한다.
float f = 1234; // 컴파일러가 int를 자동으로 float로 변환
float f = (float)1234; // 위 내용을 컴파일러가 다음처럼 해석함
byte b = 1234; // Error : byte의 표현 범위를 넘어감 (-128 ~ 127)
char ch = (char) 1000; // not Error : 명시적 형 변환
// 서로 다른 타입간의 연산에서는 두 타입 중 더 큰 크기의 타입 기준으로 변환이 발생
int i = 3;
double d = 1.0 + i; // 4.0 (not Error : 자동 형변환 발생)
참고: What's the difference between primitive and reference types?
primitive type을 제외한 모든 타입이 reference type에 해당함
primitive type변수와 다르게 크기가 정해져 있지 않다.
primitive type과 reference type은 여러 모습이 다르다.
그중 가장 큰 차이점은 primitive type 변수는 실제 값을 저장하고, reference type 변수는 인스턴스의 주소를 저장한다.
는 점이다.
Let’s assume that a class Person is already defined. If you create an int variable a, and an object reference variable person, they will store their values in memory as shown in figure 2.13.
primitive type 변수 a
, reference type 변수 person
이 있다면,
int a = 77;
Person person = new Person();
다음과 같은 모습으로 저장된다.
사진 출처: https://stackoverflow.com/questions/8790809/whats-the-difference-between-primitive-and-reference-types/32049775#32049775
변수를 3가지로 구분할 수 있다.
인스턴스에 각각 할당된 변수를 의미
클래스에 할당된 변수를 의미
클래스 변수는 모든 인스턴스들이 공유한다.
local varaibel = 모든 변수 - instance variable - class variable
class Student {
String id; // instance variable
int grade; // instance variable
Major major; // instance variable
static String profession = "student"; // class variable
Student(String id, int grade, Major major) {
this.id = id;
this.grad= grad;
this.major = major;
}
void printMessage(String message) {
// message는 local variable
System.out.println("Hello " + message + ", I'm " + this.grade);
}
}
변수의 종류에 따라 여러 가지 선언, 초기화 방법이 존재한다.
package variable;
public class MyVariable {
static int classVariable;
int instanceVariable;
public MyVariable(int a) {
MyVariable.classVariable++; // 생성자에서 class variable도 변경할 수 있다.
this.instanceVariable = a;
}
public static void main(String[] args) {
System.out.println(MyVariable.classVariable); // 아직 값 할당 X -> 기본값 0
MyVariable myVariable = new MyVariable(10); // instance, class variable 할당
System.out.println(MyVariable.classVariable); // 1
System.out.println(myVariable.instanceVariable); // 10
}
}
생성자가 인스턴스를 만드는 것이 아니다.생성자는 단순히 값을 할당하는 역할을 수행할 뿐
new
키워드가 인스턴스를 생성하고, 변수에 인스턴스의 주소를 할당한다.
인스턴스는 heap 영역에 위치한다.
변수는 stack 영역에 위치한다.
변수 선언과 동시에 값을 할당하는 방식
int x = 1;
코드로 직접 보자
class Card {
static int count;
int number;
String shape;
static { // Class Variable 초기화를 위한 class Initialization block
// 클래스가 메모리에 로딩될 때 한 번만 수행됨
count = 1;
}
{ // Instance Variable 초기화를 위한 Instance Initialization block
// Instance가 생성될 때마다 수행됨
// Constructor보다 먼저 수행된다
number++;
shape = "heart";
}
Card() {
// 생성자
}
}
여러 종류의 생성자에서 동일한 값 할당이 발생할 경우, 초기화 블록을 통해 리팩토링 가능
모든 변수는 명시적으로 타입을 명시해야 한다.
그런데 자바10 부터 자바에서도 타입 추론을 사용할 수 있다.
// 기존
String myString = "hello";
var myString2 = "hello";
System.out.println(myString2 instanceof String); // true
단, var
는 초기화 값이 존재하는 local variable에만 사용할 수 있다.
다른 언어들의 타입 추론과 달리, 자바의 var
는 한 번 타입이 정해지면 다른 타입으로 변경될 수 없다.
이는 컴파일 당시에 var
의 구체적인 타입이 결정되기 때문이다.
즉, 런타임에서 타입을 추론하는 과정이 없으므로 성능에는 큰 영향이 없다.
type[] 배열이름
형식으로 배열 변수를 선언한다.
byte[] anArrayOfBytes;
short[] anArrayOfShorts;
long[] anArrayOfLongs;
float[] anArrayOfFloats;
double[] anArrayOfDoubles;
boolean[] anArrayOfBooleans;
char[] anArrayOfChars;
String[] anArrayOfStrings;
// 다음과 같은 형식으로도 배열을 선언할 수 있으나 비권장(convention)
float anArrayOfFloats[];
new
키워드를 통해 배열 인스턴스를 생성한다.
자바에서 배열은 객체로 취급된다
// 크기가 10인 바이트 타입 값들을 저장하는 배열 변수 선언
byte[] anArrayOfBytes = new byte[10];
초기화되지 않은 배열 변수에 접근하면 컴파일 애러가 발생한다
ArrayDemo.java:4: Variable anArrayOfBytes may not have been initialized.
int[][] myInts = {
{1,2,3},
{4,5,6},
{7,8,9}
};
byte[][] myBytes = new byte[5][10] // { {10개의 default value값의 literal}, {}, {}, {}, {}}
다음과 같이 배열 인스턴스 생성과 값 할당을 동시에 수행할 수 있다.
int[] anArray = {
100, 200, 300,
400, 500, 600,
700, 800, 900, 1000
};
배열의 내장 속성인 length를 통해 배열의 크기를 알 수 있다.
System.out.println(anArray.length);
System.arraycopy
메소드를 통해 배열 복사가 가능하다
package array;
public class MyArray {
public static void main(String[] args) {
int[] myInts = {1, 2, 3, 4};
int[] yourInts = new int[4];
// 복사 대상 배열, 복사 시작 인덱스, 복사 결과 배열, 복사 시작 인덱스, 복사 개수
System.arraycopy(myInts, 0, yourInts, 0, 4);
for (int yourInt : yourInts) {
System.out.println(yourInt);
// 1
// 2
// 3
// 4
}
System.out.println(myInts); // [I@4617c264
System.out.println(yourInts); // [I@36baf30c
}
}
자바는 C언어를 따라 zero-base indexing
을 사용한다
그러므로 다음이 성립한다.
1. array 주소 = array[0] 주소
2. array[n] 주소 = array 시작 주소 + n * 데이터 타입 크기