스크롤하여 페이지 넘기기
Computer Science A · AP Computer Science A
값, 참조, 그리고 이를 사용하는 메서드
변수는 메모리에 있는 이름 붙은 상자입니다. 기본 자료형 변수는 값을 담고, 참조 변수는 객체의 위치를 담습니다.
int score = 42; int copy = score; // copy는 자신만의 42을(를) 가짐 copy = 7; // score는 여전히 42 String name = "Ada"; // 참조 String other = name; // 같은 참조
그림은 이 줄들을 실행합니다. copy = score은(는) 값 42을(를) 두 번째 상자에 복사하므로, copy이(가) 7이(가) 되어도 score은(는) 그대로입니다. name에는 문자가 하나도 들어 있지 않습니다. 이 상자에는 메모리의 다른 곳에 만들어진 String 객체를 가리키는 화살표(빨간색)가 들어 있습니다. other = name은(는) 객체가 아니라 화살표를 복사합니다. 화살표는 둘, 객체는 하나입니다.
이 차이는 Java 전체와 이 장 전체에 걸쳐 나타납니다. 변수가 무엇을 담는지, 메서드가 무엇을 받고 무엇을 돌려주는지, new이(가) 무엇을 만드는지, 그리고 String이 한번 만들어지면 왜 바뀌지 않는지가 모두 여기서 나옵니다.
이 장은 AP Computer Science A의 1단원 객체와 메서드 사용이며, 객관식 점수의 15–25%를 차지합니다. 내용은 컴파일과 오류(1.1), 자료형·식·대입(1.2–1.6), 라이브러리·문서화·메서드(1.7–1.11), 객체와 객체 생성(1.12–1.14), String(1.15)입니다. 시험의 객관식 42문항에는 Java Quick Reference가 함께 제공되며, 여기에는 사용할 수 있는 모든 라이브러리 메서드가 나와 있습니다. 자유 응답 첫 번째 문항에서는 String 메서드를 호출합니다.
그리고 세 가지 오류
알고리즘은 작업을 완수하거나 문제를 해결하는 단계별 과정입니다. 말로 쓸 수도, 그림으로 그릴 수도, 프로그램으로 쓸 수도 있습니다.
순차는 단계를 순서대로 놓은 것으로, 한 번에 하나씩 실행됩니다. Java 메서드는 흐름을 바꾸는 것이 없는 한 문장을 위에서 아래로 실행합니다.
코드는 아무 텍스트 편집기로나 쓸 수 있지만, 대부분의 프로그래머는 코드를 쓰고 컴파일하고 실행하는 도구가 있는 통합 개발 환경(IDE)을 사용합니다. 컴파일러는 소스 코드의 일부 오류를 검사하고 번역합니다. Java 컴파일러는 바이트코드를 만들고, 이것을 Java 가상 머신(JVM)이 실행합니다. 컴파일러가 찾은 오류는 프로그램을 실행하기 전에 반드시 고쳐야 합니다.
그림은 프로그램 세 개를 차례로 보내며, 각각 한 종류의 오류가 있습니다.
int total = 7 + 3 // ; 빠짐 System.out.println(total); 구문 오류는 언어의 규칙을 어긴 것입니다. 컴파일러가 이를 찾아내고(error: ';' expected) 아무것도 실행되지 않습니다.
int count = 0; System.out.println(10 / count);
런타임 오류는 프로그램이 실행되는 동안 발생하며, 대개 프로그램이 비정상적으로 끝납니다. 이것은 예외입니다. 컴파일러가 찾지 못한 오류로, 실행의 정상적인 흐름을 끊습니다(ArithmeticException: / by zero).
double avg = (7 + 8) / 2; System.out.println(avg); // prints 7.0 논리 오류는 컴파일되고 실행되지만 올바르지 않게 동작합니다. 7 and 8의 평균은 7.5이어야 합니다. 특정 데이터로 테스트하고 기대한 결과와 비교해야만 찾을 수 있습니다. (왜 7.0이(가) 출력되는지는 1.3페이지에 있습니다.)
int, double, boolean, 참조
자료형은 값의 집합과 그 값에 대한 연산입니다. 모든 자료형은 기본 자료형이거나 참조 자료형입니다.
이 과정에서는 숫자와 불 값을 위한 기본 자료형 세 가지를 사용합니다.
| type | values | examples |
|---|---|---|
int | integers | 7, -40, 0 |
double | 실수 | 2.5, 7.0, -0.01 |
boolean | true 또는 false | true |
Java에는 다른 기본 자료형이 다섯 개 더 있지만(long, short, byte, float, char), 과정과 시험 범위 밖입니다. String처럼 기본 자료형이 아닌 것은 모두 객체이며, 그 자료형은 참조 자료형입니다.
변수는 값을 담는 저장 공간이며, 프로그램이 실행되는 동안 값이 바뀔 수 있습니다. 모든 변수에는 이름과 자료형이 있고, 기본 자료형의 변수는 그 자료형의 값을 담습니다.
int n = 7; double d = 2.5; boolean ok = true; double w = 7; // stores 7.0 int m = 2.5; // 컴파일되지 않음
int는 double에 들어갑니다. double w = 7;은(는) 7.0을(를) 저장합니다. double은 int에 들어가지 않으므로 int m = 2.5;은(는) 컴파일 오류입니다. incompatible types: possible lossy conversion from double to int
값이 될 수 있는 것을 보고 자료형을 고르세요. 학생 수는 int, 가격이나 평균은 double, 전등이 켜져 있는지는 boolean입니다.
print, println, 이스케이프 시퀀스
System.out.print와 System.out.println은 화면에 정보를 표시합니다. println은 출력한 뒤 커서를 새 줄로 옮기고, print는 커서를 그대로 둡니다.
리터럴은 고정된 값을 코드로 나타낸 것입니다. 7, 2.5, true가 그 예입니다. 문자열 리터럴은 큰따옴표로 묶은 문자들의 나열입니다. "CSA"가 그 예입니다.
어떤 문자는 따옴표 안에 곧바로 입력할 수 없습니다. 이스케이프 시퀀스는 백슬래시로 시작하며 특별한 뜻이 있습니다. 이 과정에서는 세 가지를 사용합니다.
| 이스케이프 시퀀스 | means |
|---|---|
\" | 큰따옴표 |
\\ | 백슬래시 |
\n | 줄 바꿈 |
System.out.print("AP "); System.out.println("CSA"); System.out.println("say \"hi\""); System.out.print("a\\b\nc"); 그러면 콘솔에는 다음과 같이 나타납니다(그림이 한 글자씩 입력합니다).
AP CSA say "hi" a\b c 첫 번째 print는 커서를 AP 뒤에 두므로, CSA가 같은 줄에 놓입니다. 마지막 줄에서 \\는 백슬래시 하나를 출력하고, \n은 문자열 중간에서 새 줄을 시작합니다.
정수 나눗셈, 나머지, 우선순위
산술식은 숫자 값, 변수, 그리고 연산자 +, -, *, /, %를 결합한 것입니다. int 두 값을 연산하면 int가 되고, double이 하나라도 있으면 double이 됩니다.
System.out.println(17 / 5); // 3 System.out.println(17 % 5); // 2 System.out.println(17.0 / 5); // 3.4
int 두 값을 나누면 몫의 정수 부분만 남습니다. 블록 17개로는 5개씩 묶은 완전한 묶음이 3개 만들어집니다. 나머지 연산자 %는 남은 것을 구합니다. 2 나눗셈에 double이 있으면 결과는 몫 그 자체인 3.4입니다. (이 과정에서는 %를 a ≥ 0, b > 0일 때만 사용합니다.)
우선순위: *, /, %가 +, -보다 먼저 적용됩니다. 우선순위가 같은 연산자는 왼쪽에서 오른쪽으로 계산하며, 괄호는 묶는 방식을 바꿉니다. 컴파일러는 컴파일할 때 이런 방식으로 값을 연산자와 묶습니다.
| expression | value |
|---|---|
2 + 3 * 4 | 14 |
(2 + 3) * 4 | 20 |
10 - 4 - 3 | 3 |
7 + 9 % 4 | 8 |
1 / 2 * 4.0 | 0.0 |
1.0 / 2 * 4 | 2.0 |
1 / 2 * 4.0에서 1 / 2는 정수 나눗셈이므로, double이 나오기 전에 이미 0입니다.
int를 int 0으로 나누면 그 줄이 실행될 때 ArithmeticException이 발생합니다. (double로 0을 나누는 것과 그때 생기는 특별한 double 값은 과정 범위 밖입니다.)
오른쪽을 계산해 왼쪽에 저장
대입 연산자 =는 오른쪽의 식을 하나의 값으로 계산해 왼쪽의 변수에 저장합니다.
변수는 식에서 사용하기 전에 값을 받아야 하며, 그 값은 호환되는 자료형이어야 합니다. 첫 번째 대입이 변수를 초기화합니다. 이후의 대입은 값을 바꾸며, 이전 값은 사라집니다.
int a = 3; // a 3 int b = 8; // a 3, b 8 int temp = a; // a 3, b 8, temp 3 a = b; // a 8, b 8, temp 3 b = temp; // a 8, b 3, temp 3
그래서 두 변수의 값을 교환하려면 세 번째 변수가 필요합니다. temp가 없으면 a = b가 b가 가져가기 전에 a의 3을(를) 없애 버립니다. 그림은 복사본을 상자에서 상자로 옮깁니다. 원본은 자기 값을 그대로 가집니다.
프로그램이 실행될 때 식은 하나의 값으로 계산되고, 그 값에는 자료형이 있습니다. a + b는 int이고, a + 0.5는 double입니다. count = count + 1 같은 문장은 등식이 아닙니다. count를 읽어 1을 더한 다음 결과를 다시 저장합니다.
참조 변수에는 새 객체나 null을 대입할 수 있습니다. 리터럴 null은 그 참조가 어떤 객체와도 연결되어 있지 않다는 뜻입니다.
a = b = 4;처럼 식 안에서 대입하는 것은 과정 범위 밖입니다.
입력은 촉각, 청각, 시각, 텍스트일 수 있습니다. Scanner 클래스는 키보드로 입력한 텍스트를 읽는 방법 중 하나이며, 시험에서는 특정한 사용자 입력 방식을 묻지 않습니다.
버림, 확대, 반올림
형변환 연산자 (int)와 (double)은 값을 한 기본 자료형에서 다른 기본 자료형으로 바꿉니다.
double을 int로 형변환하면 버려집니다. 소수점 아래 숫자가 잘려 나가므로, 부호와 상관없이 값은 0 쪽으로 움직입니다.
| expression | value |
|---|---|
(int) 3.99 | 3 |
(int) -3.99 | -3 |
7 / 2 | 3 |
(double) 7 / 2 | 3.5 |
(double) (7 / 2) | 3.0 |
7 / 2.0 | 3.5 |
1 + 2.5 | 3.5 |
형변환은 바로 뒤에 오는 값에 적용됩니다. (double) 7 / 2는 7을 형변환한 다음 double로 나눕니다. (double) (7 / 2)는 정수 나눗셈을 먼저 하고 그 결과인 3.0을(를) 형변환합니다.
일부 코드는 int를 double로 자동으로 확대합니다. 1 + 2.5에서, 그리고 int를 double 변수에 저장할 때(1.2페이지)입니다.
double을 가장 가까운 정수로 반올림하려면, 0에서 멀어지는 쪽으로 반 단위만큼 옮긴 다음 버립니다. (int) (x + 0.5)는 0 이상인 x에, (int) (x - 0.5)는 음수인 x에 씁니다.
| x | (int) x | rounded |
|---|---|---|
2.7 | 2 | 3 |
-2.7 | -2 | -3 |
3.2 | 3 | 3 |
-1.4 | -1 | -1 |
그림은 이것들을 각각 형변환합니다. 빨간색은 0 쪽으로 버리고, 파란색은 반만큼 옮긴 다음 버립니다.
정수 오버플로와 반올림 오차
int는 정해진 크기의 메모리, 즉 4바이트에 저장되므로 범위가 있습니다. Integer.MIN_VALUE부터 Integer.MAX_VALUE까지이며, 양 끝을 포함합니다.
| constant | value |
|---|---|
Integer.MAX_VALUE | 2147483647 |
Integer.MIN_VALUE | -2147483648 |
int n = Integer.MAX_VALUE - 2; // n은 2147483645 n = n + 1; // n은 2147483646 n = n + 1; // n은 2147483647 n = n + 1; // n은 -2147483648 n = n + 1; // n은 -2147483647
식의 계산 결과가 int 범위를 벗어나면 정수 오버플로가 일어납니다. 결과는 범위 안의 int이지만 기대한 값이 아닙니다. MAX_VALUE보다 1 큰 값은 MIN_VALUE입니다. 그림은 int를 원(비율은 맞지 않음)으로 그리고 이음매를 맨 위에 둡니다. 오류 메시지는 없으며, 프로그램은 잘못된 수로 계속 진행합니다.
double은 한계가 다릅니다. double도 정해진 크기의 메모리에 저장되므로, 담을 수 있는 것보다 더 정밀한 결과는 표현할 수 있는 가장 가까운 값으로 반올림됩니다. 이것이 반올림 오차입니다.
System.out.println(0.1 + 0.2); // 0.30000000000000004 0.1도 0.2도 정확한 이진수 형태가 없어서, 작은 오차가 합에 나타납니다. 반올림 오차를 피하려면 가능한 한 int 값을 사용하세요. 돈은 달러가 아니라 센트 단위로 셉니다. (이를 피하는 특별한 십진 자료형은 과정 범위 밖입니다.)
+=, −=, *=, /=, %=, ++, −−
복합 대입 연산자는 변수의 값과 오른쪽 값 사이의 산술 연산을 한 다음, 그 결과를 변수에 대입합니다.
score += 5는 score = score + 5와 같은 일을 합니다. -=, *=, /=, %=도 마찬가지이며, int에 /=를 쓰면 정수 나눗셈입니다.
후위 증가 연산자 ++는 숫자 변수에 1을 더해 저장하고, 후위 감소 연산자 --는 1을 뺍니다.
int score = 10; // score는 10 score += 5; // score는 15 score -= 3; // score는 12 score *= 2; // score는 24 score /= 5; // score는 4 score %= 3; // score는 1 score++; // score는 2 score--; // score는 1
그림은 이 줄들을 실행하며 각 값을 테이프에 남깁니다. 종이에서도 같은 방식으로 추적하세요. 한 줄에 새 값 하나입니다.
전위 형태(++x)와 다른 식 안에서의 증가(arr[x++])는 과정 범위 밖입니다.
직접 쓰지 않은 클래스 사용하기
라이브러리는 클래스의 모음입니다. API(애플리케이션 프로그래밍 인터페이스) 명세는 프로그래머에게 그 사용법을 알려 줍니다.
클래스는 참조 자료형을 정의합니다. 라이브러리 클래스는 패키지로 묶여 있습니다. String과 Math는 java.lang에, Scanner와 ArrayList는 java.util에 있습니다. 기존 클래스는 코드를 보지 않고도 객체를 만드는 데 쓸 수 있으며, 문서화 덕분에 사용할 수 있습니다.
클래스의 속성은 그 클래스와 관련된 데이터로, 변수에 저장됩니다. String의 문자들이 그 예입니다. 클래스의 행동은 인스턴스가 할 수 있는 일, 또는 인스턴스에 할 수 있는 일이며, 메서드로 정의됩니다. length()와 substring이 그 예입니다.
주석은 코드를 처음 쓴 프로그래머와 다른 사람에게 코드를 설명합니다. 컴파일러는 주석을 무시하고 주석은 실행되지 않습니다. Java에는 세 종류가 있습니다.
// a comment to the end of the line /* a block of comment, 여러 줄에 걸친 */ /** a Javadoc comment, used to make API documentation */
그림은 String을 선반에서 꺼내 substring의 문서를 펼칩니다.
사전 조건은 메서드가 기대한 대로 동작하려면 메서드가 실행되기 직전에 참이어야 하는 조건입니다. 메서드가 이를 검사하리라고 기대하지는 않습니다. substring을 호출할 때 to가 끝을 넘으면 사전 조건을 어기게 되고, 그 결과는 친절한 답이 아니라 예외입니다.
사후 조건은 메서드가 실행된 뒤에 언제나 참이어야 하는 조건입니다. 반환되는 값이나 이후 객체 속성의 값처럼 결과를 설명합니다.
매개변수, 인수, 반환값
메서드는 호출될 때만 실행되는, 이름 붙은 코드 블록입니다. 블록은 중괄호로 묶은 모든 코드입니다.
절차 추상화: 메서드가 어떻게 쓰였는지 몰라도 무엇을 하는지만 알면 사용할 수 있습니다. 헤더에 필요한 정보가 나와 있습니다.
public static double pow(double base, double exponent)
매개변수는 헤더에 선언되어 메서드 안에서 쓰이는 변수입니다(base, exponent). 메서드 시그니처는 메서드의 이름과 매개변수 자료형의 순서 있는 목록입니다. pow(double, double)이 그 예입니다. 매개변수가 없는 메서드는 목록이 비어 있습니다. length()가 그 예입니다.
인수는 메서드를 호출할 때 전달하는 값입니다. 인수는 매개변수와 개수와 순서가 같아야 합니다. 인수는 값으로 전달됩니다. 각 매개변수는 인수의 복사본으로 시작합니다. 그림에서 2과(와) 10은(는) 복사본 2.0과(와) 10.0이(가) 됩니다.
double r = Math.pow(2, 10); System.out.println(r); // 1024.0 void가 아닌 메서드는 반환형의 값을 반환합니다. 이를 쓰려면 변수에 저장하거나 식에 사용합니다. void 메서드는 아무것도 반환하지 않으므로 식의 일부가 될 수 없습니다. int x = System.out.println(5);는 컴파일되지 않습니다.
이름이 같고 시그니처가 다른 메서드가 여러 개 있으면 메서드가 오버로딩되었다고 합니다. Math.abs(int)와 Math.abs(double)이 그 예입니다. 인수에 따라 어느 것이 실행될지 정해집니다.
호출은 문장의 순서를 끊습니다. 메서드의 문장이 먼저 실행되고, 마지막 문장(또는 return)이 실행되면 제어가 호출 바로 다음으로 돌아옵니다.
정적 메서드와 Math.random()
클래스 메서드는 특정 객체가 아니라 클래스에 속합니다. 헤더에 키워드 static이 있고, 클래스 이름과 점 연산자로 호출합니다. Math.sqrt(2.0)이 그 예입니다. 메서드를 정의한 클래스 안에서는 클래스 이름을 생략할 수 있습니다.
Math 클래스는 java.lang에 있어서 import 없이 쓸 수 있으며, 클래스 메서드만 있습니다. Java Quick Reference에는 다섯 가지가 나와 있습니다.
| method | returns |
|---|---|
static int abs(int x) | int의 절댓값 |
static double abs(double x) | double의 절댓값 |
static double pow(double base, double exponent) | base의 exponent제곱 |
static double sqrt(double x) | 음이 아닌 제곱근 |
static double random() | 0.0 이상 1.0 미만의 double |
Math.random()의 범위는 0.0을 포함하고(포함 끝점) 1.0을 포함하지 않습니다(제외 끝점). 산술과 형변환으로 원하는 범위로 옮깁니다.
int roll = (int) (Math.random() * 6) + 1;
그림은 단계별로 보여 줍니다. * 6는 [0.0, 1.0)을 [0.0, 6.0)으로 늘립니다. (int)는 각 부분을 0, 1, …, 5(으)로 버려서 6개의 같은 부분이 되므로 6개의 값이 똑같이 나올 수 있습니다. + 1는 이를 1, …, 6(으)로 옮깁니다. 일반적으로 (int) (Math.random() * count) + low는 count개의 int 중 하나를 주며, 그 시작은 low입니다.
30번 굴린 결과는 5 on 1, 5 on 2, 6 on 3, 4 on 4, 6 on 5, 4 on 6이었습니다. 짧게 반복하면 똑같이 나올 수 있다는 것이 똑같이 자주 나온다는 뜻은 아닙니다.
설계도와 그것이 만드는 것
클래스는 객체의 속성과 행동을 정식으로 구현한 것, 곧 설계도입니다. 객체는 클래스의 구체적인 인스턴스로, 그 속성에 대해 자신만의 값을 가집니다.
그림은 가상의 클래스 Book을 사용합니다. 속성은 두 가지(제목과 쪽수)이고 행동도 두 가지(getTitle()과 getPages())입니다. 이 클래스로 찍어 낸 Book마다 자기 제목과 쪽수가 있습니다. "지도", 120; "새", 340; "별", 96
관련된 클래스들은 공통 속성과 행동을 한 클래스, 곧 상위 클래스에 두어 함께 쓸 수 있습니다. 이를 확장한 클래스인 하위 클래스는 같은 코드를 다시 쓰지 않고도 그 속성과 행동을 이어받습니다. 이것이 상속 관계입니다. (상속을 설계하고 쓰는 것은 과정 범위 밖입니다.)
Java의 모든 클래스는 Object 클래스의 하위 클래스이므로, 모든 객체에 toString()이나 equals 같은 메서드가 있습니다.
참조 자료형 변수는 객체 참조를 담습니다. 객체의 메모리 주소라고 생각할 수 있으며, 그림의 화살표가 이것이고 객체 자체는 아닙니다.
Book b; // b는 Book에 대한 참조를 담을 수 있음 String s; // s는 String에 대한 참조를 담을 수 있음
new, 생성자, null
객체는 키워드 new 뒤에 클래스의 생성자 중 하나를 호출해 만듭니다.
생성자의 이름은 클래스 이름과 같습니다. 생성자의 시그니처는 그 이름과 매개변수 자료형의 순서 있는 목록이며, 헤더에는 매개변수의 자료형과 이름이 나옵니다. 가상의 Book 문서에는 다음이 나와 있습니다.
Book(String title, int pages) Book(String title) String getTitle() int getPages()
시그니처가 다른 생성자가 두 개 있습니다. 생성자가 오버로딩되었으며, 인수에 따라 어느 것이 호출될지 정해집니다. new Book("지도")은 두 번째를 호출합니다.
Book b = new Book("지도", 120); Book c = null; 그림은 첫 번째 줄을 순서대로 실행합니다. new가 Book을 위한 메모리를 만들고, 생성자가 실행되어 매개변수가 속성의 초깃값을 정합니다. 그다음 새 객체에 대한 참조가 b에 저장됩니다. 메서드 호출처럼 생성자 호출도 문장의 순서를 끊으며, 마지막 문장이 실행되면 제어가 호출 바로 다음으로 돌아옵니다.
생성자 인수는 매개변수 자료형과 개수와 순서가 맞아야 하며 값으로 전달됩니다. new Book(120, "지도")는 int 다음에 String을 받는 생성자가 없으므로 컴파일되지 않습니다.
참조 자료형 변수는 객체 참조를 담거나, 객체가 없으면 null을 담습니다. c는 아무것도 가리키지 않습니다.
점 연산자와 null
인스턴스 메서드는 클래스의 객체에 대해 호출하며, 객체 이름, 점 연산자, 메서드를 씁니다. b.getPages()가 그 예입니다.
int p = b.getPages(); // p는 120 int q = c.getPages(); // NullPointerException
호출은 b에 있는 참조를 따라 그 객체로 가서 그 객체에 대해 getPages()를 실행하고, 반환된 int(120)는 p에 저장됩니다. 다른 Book 객체라면 자신의 쪽수로 답할 것입니다.
c는 null입니다. 화살표를 따라갈 객체가 없습니다. null 참조에 메서드를 호출하면 그 줄이 실행될 때 NullPointerException이 발생합니다. 컴파일은 됩니다. 컴파일러는 Book에 getPages()가 있는지만 확인하기 때문입니다.
1.10페이지의 클래스 메서드와 비교해 보세요. Math.sqrt(x)는 클래스에 대해 호출하고, s.length()는 String 객체 하나에 대해 호출하여 그 문자열에 대해서만 답합니다.
불변이며, 연결하면 새 객체가 만들어짐
String 객체는 문자들의 나열을 나타냅니다. 문자열 리터럴 "cat"으로 만들거나 생성자 new String("cat")으로 만듭니다. String은 java.lang에 있어서 기본으로 사용할 수 있습니다.
String 객체는 불변입니다. 한번 만들어지면 그 문자는 바뀌지 않습니다. String에 대해 호출한 메서드는 그것을 바꾸지 않고 새 값을 반환합니다.
String s = "cat"; // s은(는) "cat" s = s + "s"; // s은(는) "cats" String t = s + 1 + 2; // t은(는) "cats12" String v = 1 + 2 + s; // v은(는) "3cats"
+와 +=는 String을 연결하여 새 String 객체를 만듭니다. 그림에서 s = s + "s"는 "cats"을(를) 만들어 s가 그것을 가리키게 하고, "cat" 객체는 여전히 "cat" 그대로입니다.
기본 자료형을 String과 연결하면 String으로 바뀝니다. +는 왼쪽에서 오른쪽으로 계산하므로 String이 처음 나오는 위치가 중요합니다. s + 1 + 2에서는 각 숫자가 텍스트로 이어 붙고, 1 + 2 + s에서는 1 + 2를 int로 먼저 더합니다.
어떤 객체든 String과 연결할 수 있습니다. Java는 그 객체의 toString 메서드를 호출하며, 이 메서드는 모든 클래스가 Object로부터 물려받고 객체를 나타내는 문자열을 반환합니다. 하위 클래스는 흔히 이것을 오버라이딩합니다(상위 클래스와 시그니처가 같고 자신만의 동작을 하는 public 메서드). 이런 오버라이딩을 직접 쓰는 것은 과정 범위 밖입니다.
인덱스, 길이, substring, indexOf
String의 문자에는 0부터 길이보다 1 작은 값까지의 인덱스가 있습니다. 이 범위를 벗어난 인덱스를 쓰면 StringIndexOutOfBoundsException이 발생합니다.
Java Quick Reference에는 다음 String 메서드(와 생성자)가 나와 있습니다. split은 4.6 주제의 텍스트 파일과 함께 나옵니다.
| method | returns |
|---|---|
int length() | 문자의 개수 |
String substring(int from, int to) | 인덱스 from부터 인덱스 to - 1까지의 문자들 |
String substring(int from) | substring(from, length()) |
int indexOf(String str) | str가 처음 나오는 위치의 인덱스. 없으면 −1 |
boolean equals(Object other) | 다음 페이지 |
int compareTo(String other) | 다음 페이지 |
String s = "BANANAS"; s.length(); // 7 s.substring(1, 4); // "ANA" s.substring(4); // "NAS" s.indexOf("NA"); // 2 s.indexOf("X"); // -1 s.substring(3, 4); // "A"
to는 제외되는 첫 번째 인덱스이므로 substring(from, to)에는 to - from개의 문자가 있습니다. indexOf는 처음 나온 위치만 찾습니다. "NA"는 인덱스 4에서도 시작합니다.
index에 있는 문자 하나만 담은 문자열은 substring(index, index + 1)입니다.
equals, compareTo, 그리고 ==가 아닌 이유
equals는 두 String이 같은 문자열을 가지고 있는지 묻습니다. compareTo는 어느 쪽이 먼저인지 묻습니다.
String a = "pear"; String b = new String("pear"); System.out.println(a == b); // false System.out.println(a.equals(b)); // true System.out.println(a.compareTo("peach") > 0); // true a와 b는 같은 문자를 가진 서로 다른 두 객체를 참조합니다. a.equals(b)는 문자를 쌍마다 비교하므로 true입니다. a == b는 참조를, 곧 같은 객체를 가리키는지를 비교하므로 false입니다. String은 equals로 비교하세요. (==로 참조를 비교하는 내용은 2.6 주제에서 다시 나옵니다.)
a.compareTo(other)는 a가 other보다 앞이면 0보다 작은 값을, 둘이 같으면 0을, a가 뒤이면 0보다 큰 값을 반환합니다. String은 알파벳 순서로 정렬되며, 처음으로 다른 문자가 순서를 정합니다. "pear"과(와) "peach"은(는) 인덱스 3까지 같고, 거기서 'r'이(가) 'c'보다 뒤에 오므로 결과는 양수입니다. 부호만 믿으세요. Quick Reference가 보장하는 것은 그것뿐입니다.
이 과정에서는 equals를 String과 다른 String을 비교하는 데만 사용합니다.