azul JDK 11(LTS) 버전으로 진행된다.
컴퓨터의 기억 과정
기억하고자 하는 값이 얼만큼의 메모리 공간을 필요로 하는지 파악한다.
기억하고자 하는 값이 차지하는 용량만큼의 메모리 공간을 확보한다.
값을 저장한 공간에 이름을 붙인다.
확보한 메모리 공간에 기억하고자 하는 값을 저장한다.
1~3 과정 = 변수 선언
4 과정 = 변수에 값을 할당
값이 변할 수 있는 데이터를 임시적으로 저장하기 위한 수단
컴퓨터는 메모리에 데이터를 저장한다. 각 메모리셀에는 고유 번호가 오름차순으로 매겨져 있고, 이 고유 번호를 메모리 주소라고 한다.
아파트를 예로 들어 메모리와 비교해보자.
메모리 | 아파트 |
---|---|
메모리셀의 집합체 | 집의 집합체 |
각 메모리셀에 데이터를 저장할 수 있다. | 각 집에 사람이 들어가 살 수 있다. |
각 메모리셀을 가리키는 주소가 있다. 예 : 0xf38d2a6c | 각 집을 가리키는 호수가 있다. 예 : 1401호 |
이해를 돕기 위한 의사코드(실제로 동작하진 않음)
class Example {
public static void main(String[] args) {
0xf38d2a6c = 1; // 값 저장
출력(0xf38d2a6c) // 값 사용
}
}
class Example {
public static void main(String[] args) {
0xf38d2a6c = 1;
0xa92d42b2 = 2;
0x24de241c = 3;
...
}
}
이런 구조의 툴로 프로그래밍하면 발생하는 문제점
저장해야 할 값이 많을 때, 사람이 메모리 주소를 식별하기 어렵다.
시스템 운영에 꼭 필요한 데이터를 실수로 덮어쓸 가능성이 존재한다.
따라서 변수를 통해 아래와 같이 값을 저장할 수 있다.
class Example {
public static void main(String[] args) {
int num1 = 1;
int num2 = 2;
int num3 = 3;
...
}
}
값을 저장할 수 있는 메모리 공간을 확보하고, 사람이 식별할 수 있는 이름을 붙인 것
class Example {
public static void main(String[] args) {
int num; // 변수 선언
}
}
변수를 선언하면 컴퓨터는 값을 저장할 메모리 공간을 확보하고, 확보한 메모리 공간에 사용자가 입력한 변수 이름으로 이름을 붙인다.
컴퓨터에 저장할 수 있는 값은 여러 가지 종류로 나뉘는데, 이러한 값의 종류를 타입이라고 하며, 여러 타입들 중에는 int형이라는 타입이 있다. int형은 4byte의 메모리 공간을 차지 하며, 정수형 타입이다.
int num;에서 int는 컴퓨터에게 아래의 내용을 암시한다.
변수 num은 int형이다. → int형은 4byte다. → 따라서 4byte의 메모리 공간을 확보해야 한다.
정리하면, 변수를 선언한다는 것은 어떤 값을 저장할 메모리 공간을 확보하고, 해당 메모리 공간을 식별할 수 있는 이름을 붙이는 것을 의미한다.
class Example {
public static void main(String[] args) {
int num; // 변수 선언
num = 1; // 값 할당
}
}
변수를 선언하고 나서 처음으로 값을 할당하는 것을 초기화라고 한다.
class Example {
public static void main(String[] args) {
int num; // 변수 선언
num = 1; // 값 할당(초기화)
num = 2; // 값 할당(재할당)
}
}
위와 같이 재할당 가능
class Example {
public static void main(String[] args) {
int num = 1; // 선언과 동시에 초기화
}
}
위와 같이변수를 선언하면서 동시에 초기화할 수도 있음
일반적으로 두 번째 단어부터 대문자로 시작해 구분한다.= 카멜 케이스(camelCase)
int camelCase;
변수명으로 영문자, 숫자, _, $를 사용할 수 있으며, 영문자는 대소문자가 구별되어 인식됨.
int num;
int NUM; // num과 다른 변수입니다.
int num_1;
int num$1;
숫자로 시작하는 변수명은 사용할 수 없다.
int 10Age; // Error
자바에서 이미 사용 중인 예약어(reserved word)는 변수명으로 사용할 수 없다.
int byte; // Error
int class; // Error
컴퓨터가 이해할 수 있는 코드는 어느 바보나 다 짤 수 있다. 훌륭한 프로그래머는 사람이 이해할 수 있는 코드를 짠다.
[리팩토링] 저자 마틴 파울러
읽기 좋은 코드는 잘 지은 변수명에서 시작한다.
변하지 말아야 할 데이터를 임시적으로 저장하기 위한 수단
final
이라는 키워드를 사용해 선언할 수 있다.final double CALCULATOR_PI = 3.14;
프로그램이 실행되면서 값이 변하면 안되는 경우
코드 가독성을 높이고 싶은 경우
코드 유지관리를 손쉽게 하고자 하는 경우
Q: 상수를 사용하는 이유 중 코드 유지관리를 손쉽게 하고자 하는 경우에서
pi값을 예로 들며 pi값 3.14에서 3.141592 로 바꿀 때 변수값을 일일이 바꿔주지 않아도
되기 때문에 편하다고 나와있는데 이건 변수에 값을 할당해서 써도 마찬가지 아닌가요??
int pi = 3.14; ->int pi = 3.141592; 이렇게 바꾸면 pi라는 변수명이 사용된 모든 계산식에 적용되잖아요..
A: 지금은 간단한 케이스 밖에 없어서 그렇게 생각할 수 있다.
여러 메서드가 얽히며 실수로 값을 할당 할 수 있고 같은 이름의 변수를 다른 클래스에서 새롭게 선언해 버리는 등 문제가 발생할 수 있다.
어떤 값의 유형 및 종류를 의미하며, 타입에 따라 값이 차지하는 메모리 공간의 크기와, 값이 저장되는 방식이 결정된다.
값이 차지하는 메모리 공간의 크기
값이 저장되는 방식
이해를 돕기 위해 의사코드(실제 코드 x)
class Example {
public static void main(String[] args) {
기본타입변수 = 1;
참조타입변수 = 객체;
출력(기본타입변수);
출력(참조타입변수);
}
}
예시 코드
public class Example {
public static void main(String[] args) {
int primitive = 1;
Object reference = new Object();
System.out.println(primitive);
System.out.println(reference);
}
}
싱행 결과
- 사전적으로 리터럴(Literal)은 ‘문자 그대로의'라는 뜻
- 프로그래밍에서 리터럴이란 문자가 가리키는 값 그 자체
class Example {
public static void main(String[] args) {
int num; // 변수 선언
num = 1; // 값 할당
}
}
위 예제에서 num에 할당하고 있는 1이 바로 리터럴이다.
// 정수형 리터럴 20을 정수형 변수 currentAge에 할당
int currentAge = 20;
// 실수형 리터럴 3.14159를 실수형 변수 pi에 할당
double pi = 3.14159;
// 논리형 리터럴 true를 논리형 변수 boolean에 할당
boolean isGenius = true;
// 문자형 리터럴 'A'를 문자형 변수 firstAlphabet에 할당
char firstAlphabet = 'A';
// 문자열 리터럴 "CodeStates"를 문자열 타입 변수 learnedAt에 할당
String learnedAt = "CodeStates";
리터럴을 사용할 때 알아두어야 하는 두가지.
float 타입의 변수에 실수형 리터럴을 할당할 때, 리터럴 뒤에 접미사 f를 붙여주어야 한다.
long 타입의 변수에 정수형 리터럴을 할당할 때, 리터럴 뒤에 접미사 L을 붙여주어야 한다.
float weight = 74.5f;
final long LIGHT_YEAR = 9460730472580L;
float = 9460730472580.0F;
double = 9460730472580.0D;
숫자를 나타내는 타입으로, byte, short, int, long 의 총 4개의 타입으로 분류
예전에는 메모리의 용량이 넉넉하지 않아서 필요에 따라 변수의 자료 범위를 변경 했었다. 근래에는 일반적으로 int형을 사용한다.
타입별로 차지하는 메모리 공간의 크기와 표현할 수 있는 범위
정수형 리터럴은 아래와 같이 정수형 변수에 할당할 수 있다.
// 각 데이터 타입의 표현 범위에 맞는 값을 할당하고 있습니다.
byte byteNum = 123;
short shortNum = 12345;
int intNum = 123456789;
long longNum = 12345678910L;
// 각 데이터 타입의 표현 범위에 벗어난 값을 할당하고 있어 에러가 발생합니다.
byte byteNum = 130;
short shortNum = 123456;
int intNum = 12345678910;
// 숫자가 길면 언더바로 구분할 수 있습니다.
int intNum = 12_345_678_910;
long longNum = 12_345_678_910L;
어떤 값이 실수로 작성한 코드에 의해 각 타입의 표현 범위를 넘어서는 경우가 발생할 수 있다.
byte형
오버플로우
언더플로우
데이터 타입의 크기가 데이터의 표현 범위를 결정한다.
소수점을 가지는 값을 의미하며, float형과 double형으로 분류
double형 리터럴에는 접미사 d를 붙여도, 붙이지 않아도 되지만, float형 리터럴에는 반드시 접미사 f를 붙여주어야 한다.
// float형 리터럴을 float형 변수에 할당
float num1 = 3.14f;
// double형 리터럴을 double형 변수에 할당
double num2 = 3.141592d;
double num2 = 3.141592;
double형은 float형보다
더 큰 실수를 저장할 수 있다.
더 정확하게 저장할 수 있다.
실수형에서도 오버플로우와 언더플로우가 발생한다. 다만, 결과가 다르다.
오버플로우
언더플로우
논리 타입의 종류는 boolean형 한가지 뿐이다.
boolean
형
참 또는 거짓을 저장할 수 있는 데이터 타입으로, 오직 true 혹은 false를 값으로 가진다.
cf) 단순히 참과 거짓을 표현하기 위해서는 1bit만 있으면 되지만, JVM이 다룰 수 있는 데이터의 최소 단위가 1byte이기 때문에 boolean형은 1byte(8bit)의 크기를 가진다.
boolean isRainy = true;
boolean isAdult = false;
문자 타입은 2byte 크기의 char형 오직 하나만 있다.
문자 타입 변수를 선언하면 해당 변수에 오직 하나의 문자형 리터럴을 저장할 수 있다.
문자형 리터럴을 작성할 때에는 반드시 큰 따옴표(”A”)가 아닌 작은 따옴표(’A’)를 사용하여야 한다.
(큰 따옴표를 사용한 리터럴은 문자형 리터럴이 아니라 문자열 리터럴로 인식되기 때문)
자바에서 문자형과 문자열은 다르다.
char letter1 = 'a';
char letter2 = 'ab'; // 에러 : 단 하나의 문자만 할당할 수 있다.
char letter3 = "a" // 에러 : 작은 따옴표를 사용해야 한다.
숫자를 문자형 변수에 할당할수도 있수도 있다.
char letter = 65;
System.out.print(letter); // 출력 결과 : A
boolean을 제외한 기본 타입 7개는 서로 타입을 변환할 수 있으며, 자동으로 타입이 변환되는 경우도 있고, 수동으로 변환해주어야만 하는 경우도 있다.
아래 두 경우에는 타입이 자동으로 변환된다.
바이트 크기가 작은 타입에서 큰 타입으로 변환할 때 (ex. byte → int)
덜 정밀한 타입에서 더 정밀한 타입으로 변환할 때 (ex. 정수 → 실수)
아래 순서도의 화살표는 화살표를 기준으로 좌측의 타입이 우측의 타입으로 자동으로 변환될 수 있음을 의미한다.
소괄호 안 내용 = 타입이 가지는 크기
byte(1) -> short(2)/char(2) -> int(4) -> long(8) -> float(4) -> double(8)
위의 순서도에서 float은 4byte인데 int와 long보다 더 뒤쪽에 있는 것을 볼 수 있다. 이는 float이 표현할 수 있는 값이 모든 정수형보다 더 정밀하기 때문이다.
// float이 long보다 정밀하므로, 자동으로 타입 변환
long longValue = 12345L;
float floatValue = longValue;
System.out.println(floatValue); // 12345.0이 출력
차지하는 메모리 용량이 더 큰 타입에서 작은 타입으로는 자동으로 타입이 변환되지 않기 때문에 수동으로 타입을 변환해주어야 하고, 이를 캐스팅(casting)이라고 한다.
수동으로 타입을 변환할 때에는 캐스팅 연산자 ()를 사용하며, 캐스팅 연산자의 괄호 안에 변환하고자 하는 타입을 적어주면 된다.
//int 타입으로 선언된 변수 intValue를 더 작은 단위인 byte로 변환합니다.
int intValue = 128;
byte byteValue = (byte)intValue;
System.out.println(byteValue); // -128
byte형의 표현 범위는 -128 ~ 127이므로, 오버플로우가 발생해 값이 -128이 되는 것을 확인할 수 있다.
기본적으로 String 타입은 큰따옴표("")로 감싸진 문자열을 의미한다.
// 문자열 리터럴을 String 타입의 변수 name에 할당하는 방법
String name1 = "Kim Coding";
// String 클래스의 인스턴스를 생성하는 방법
String name2 = new String("Kim Coding");
String 타입의 변수는 String 변수명;으로 선언할 수 있다.
선언한 변수에 문자열을 할당하는 방법은 두 가지가 있다.
그러나, 문자열을 출력해보면 주소값이 아니라 문자열의 내용이 출력된다.
String name1 = "Kim Coding";
String name2 = new String("Kim Coding");
System.out.print(name1); // "Kim Coding"
System.out.print(name2); // "Kim Coding"
이는 String 타입의 변수를 참조하면 String 클래스의 메서드인 toString()
이 자동으로 호출되기 때문이다.
toString()
이 호출되면 String 타입의 변수가 저장하고 있는 주소값에 위치한 String 인스턴스의 내용을 문자열로 변환해준다.
String name1 = "Kim Coding";
String name2 = "Kim Coding";
String name3 = new String("Kim Coding");
String name4 = new String("Kim Coding");
boolean comparison1 = name1 == "Kim Coding"; // true
boolean comparison2 = name1 == name2; // true
boolean comparison3 = name1 == name3; // false
boolean comparison4 = name3 == name4; // false
boolean comparison5 = name1.equals("Kim Coding"); // true
boolean comparison6 = name1.equals(name3); // true
boolean comparison7 = name3.equals(name4); // true
name1과 name2는 a방법을 사용하고 있다. 이처럼 동일한 문자열 리터럴을 두 변수에 할당하는 경우, 두 변수는 같은 문자열의 참조값을 공유한다. 즉, 저장하게 되는 문자열의 주소값이 같다.
반면, name3과 name4는 b방법을 사용하고 있다. 이처럼 String 클래스의 인스턴스를 생성하게 되면 문자열의 내용이 같을지라도, 별개의 인스턴스가 따로 생성된다. 즉, 저장하게 되는 인스턴스 주소값이 다르다.
cf)
등가 비교 연산자는 좌항 == 우항
의 형태로 사용할 수 있으며, 좌항의 값과 우항의 값이 일치하는지 검사하여 일치하면 true를, 일치하지 않으면 false를 반환함.
equals()
메서드는 .
앞의 변수가 저장하고 있는 문자열의 내용과 () 안의 문자열의 내용이 같은지 비교하여 같으면 true를 다르면 false를 반환
charAt()
메서드는 해당 문자열의 특정 인덱스에 해당하는 문자를 반환한다.
String str = new String("Java");
System.out.println("문자열 : " + str); // "문자열 : Java"
System.out.println(str.charAt(0)); // 'J'
System.out.println(str.charAt(1)); // 'a'
System.out.println(str.charAt(2)); // 'v'
System.out.println(str.charAt(3)); // 'a'
System.out.println("\ncharAt() 메서드 호출 후 문자열 : " + str);
compareTo()
메서드는 해당 문자열을 인수로 전달된 문자열과 사전 편찬 순으로 비교한다. (대소문자를 구분하여 비교)
만약 두 문자열이 같다면 0을 반환하며, 해당 문자열이 인수로 전달된 문자열보다 작으면 음수를, 크면 양수를 반환한다.
만약 문자열을 비교할 때 대소문자를 구분하지 않기를 원한다면, compareToIgnoreCase() 메서드를 사용하면 된다.
String str = new String("abcd");
System.out.println("문자열 : " + str);
System.out.println(str.compareTo("bcef"));
System.out.println(str.compareTo("abcd") + "\n");
// "\n" = 한줄 띄기 위한 용도
System.out.println(str.compareTo("Abcd"));
System.out.println(str.compareToIgnoreCase("Abcd"));
System.out.println("compareTo() 메서드 호출 후 문자열 : " + str);
concat()
메서드는 해당 문자열의 뒤에 인수로 전달된 문자열을 추가한 새로운 문자열을 반환한다.
( 만약 인수로 전달된 문자열의 길이가 0이면, 해당 문자열을 그대로 반환 )
String str = new String("Java");
System.out.println("문자열 : " + str);
System.out.println(str.concat("수업"));
System.out.println("concat() 메서드 호출 후 문자열 : " + str);
indexOf()
메서드는 해당 문자열에서 특정 문자나 문자열이 처음으로 등장하는 위치의 인덱스를 반환한다.
( 만약 해당 문자열에 전달된 문자나 문자열이 포함되어 있지 않으면 -1을 반환 )
String str = new String("Oracle Java");
System.out.println("문자열 : " + str);
System.out.println(str.indexOf('o'));
System.out.println(str.indexOf('O'));
System.out.println(str.indexOf('a'));
System.out.println(str.indexOf("Java"));
System.out.println("indexOf() 메서드 호출 후 원본 문자열 : " + str);
trim()
메서드는 해당 문자열의 맨 앞과 맨 뒤에 포함된 모든 공백 문자를 제거해 준다.
String str = new String(" Java ");
System.out.println("문자열 : " + str);
System.out.println(str + '|');
System.out.println(str.trim() + '|');
System.out.println("trim() 메서드 호출 후 문자열 : " + str);
toLowerCase()
메서드는 해당 문자열의 모든 문자를 소문자로 변환시켜 준다.
toUpperCase()
메서드는 해당 문자열의 모든 문자를 대문자로 변환시켜 준다.
String str = new String("Java");
System.out.println("문자열 : " + str);
System.out.println(str.toLowerCase());
System.out.println(str.toUpperCase());
System.out.println("두 메서드 호출 후 문자열 : " + str);
StringTokenizer 클래스는 문자열을 우리가 지정한 구분자로 문자열을 쪼개주는 클래스다. 그렇게 쪼개어진 문자열을 우리는 토큰(token)이라고 부른다.
StringTokenizer를 사용하기 위해서는 java.util.StringTokenizer를 import해야한다.
( 인텔리제이 import 단축키 = ctrl + alt + o )
package test1;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args){
String str = "This is a string example using StringTokenizer";
StringTokenizer tokenizer = new StringTokenizer(str);
System.out.println(str); // << This is a string example using StringTokenizer
System.out.println(); // << 공백 한줄 띄기용
System.out.println("total tokens:"+tokenizer.countTokens()); // << total tokens:7
while(tokenizer.hasMoreTokens()){ // << 단어 하나씩 출력
System.out.println(tokenizer.nextToken());
}
System.out.println("total tokens:"+tokenizer.countTokens()); // << total tokens:0
}
}
int countTokens()
boolean hasMoreElements(), boolean hasMoreTokens()
String nextToken(), Object nextElement()
한번 생성된 String 클래스의 인스턴스는 여러 개의 문자열을 더할 때 매번 새로운 인스턴스를 생성해야 한다. 수행횟수가 늘어날 수록 비효율적이다. 이럴 때 사용하는게 StringBuilder다.
public class Main {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("문자열 ").append("연결");
String str = stringBuilder.toString();
System.out.println(stringBuilder);
System.out.println(str);
}
}
StringBuilder의 객체를 생성한 후, append()의 인자로 연결하고자 하는 문자열을 넣어서 StringBuilder의 객체를 통해 호출한다.
문자열을 출력할 때, 그리고 변수에 문자열을 할당할 때에는 toString()메서드를 사용하면된다.
String 클래스의 인스턴스는 한 번 생성되면 그 값을 읽기만 할 수 있고, 변경할 수는 없다. 하지만 StringBuffer 클래스의 인스턴스는 그 값을 변경할 수도 있고, 추가할 수도 있다.
인수로 전달된 값을 문자열로 변환한 후, 해당 문자열의 마지막에 추가하는 메서드
StringBuffer str = new StringBuffer("Java");
System.out.println("문자열 : " + str); // << 문자열 : Java
System.out.println(str.append(" programming")); // << Java programming
System.out.println("append() 메서드 호출 후 문자열 : " + str); // << append() 메서드 호출 후 문자열 : Java programming
StringBuffer 인스턴스의 현재 버퍼 크기를 반환하는 메서드
(StringBuffer str01 = new StringBuffer();
<< 이렇게 기본형 쓰면 여유 버퍼 크기인 16이 남음. 문자 1당 1 ex. "Java" = 4)
전달된 인덱스에 해당하는 부분 문자열을 해당 문자열에서 제거하는 메서드
인수로 전달된 값을 문자열로 변환한 후, 해당 문자열의 지정된 인덱스 위치에 추가하는 메서드
(전달된 인덱스 위치가 해당 문자열의 길이와 같으면, append() 메서드와 같은 결과를 반환한다.
지시자 개념을 공부하고 퀴즈를 풀었으면 좋았을 것 같다.
문자열 인덱스 위치는 0부터 시작한다.
ex)
Java oracle
0123 4 5678910