java 공부 2024.05.09
생성자
파라미터에 아무것도 안주면 에러가 남
레퍼런스변수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package today0509;
// 클래스 AvangerTest의 정의 시작
public class AvangerTest {
// main 메소드 시작: 프로그램의 시작점
public static void main(String[] args) {
// Avenger 클래스의 객체 thor를 생성, 이름은 "토르", 체력은 150
Avenger thor = new Avenger("토르", 150);
// Avenger 클래스의 객체 thanos를 생성, 이름은 "타노스", 체력은 160
Avenger thanos = new Avenger("타노스", 160);
// thor가 thanos에게 펀치를 가함 (thanos의 체력이 10 감소)
thor.punch(thanos);
// thor가 thanos에게 다시 펀치를 가함 (thanos의 체력이 다시 10 감소)
thor.punch(thanos);
// thanos가 thor에게 펀치를 가함 (thor의 체력이 10 감소)
thanos.punch(thor);
}
// main 메소드 끝
}
// Avenger 클래스의 정의 시작
class Avenger {
// Avenger의 이름을 저장하는 String 타입의 인스턴스 변수 name
String name;
// Avenger의 체력을 저장하는 int 타입의 인스턴스 변수 hp
int hp;
// Avenger 클래스의 생성자, 이름과 체력을 초기화
Avenger(String s, int i) {
name = s; // 이름 초기화
hp = i; // 체력 초기화
}
// punch 메소드 정의: 다른 Avenger 객체를 매개변수로 받아 펀치를 가하는 메소드
void punch(Avenger enemy) {
// 펀치하는 Avenger의 이름을 출력
System.out.printf("[%s]의 펀치!", name);
// enemy Avenger의 체력을 10 감소
enemy.hp -= 10;
// enemy Avenger의 남은 체력을 출력
System.out.printf(" -> %s의 체력 : %d\n", enemy.name, enemy.hp);
}
// punch 메소드 끝
}
// Avenger 클래스의 정의 끝
160 체력이 리셋이 안되는 이유 :
객체의 상태가 메소드 호출에 따라 지속적으로 변경되기 때문.
Java에서 객체의 상태는 필드(예: 변수)에 의해 저장되며, 객체의 메소드를 통해 그 상태가 변경될 수 있다. 여기서 Avenger
클래스의 hp
필드는 Avenger
객체의 체력을 나타냄. 객체의 메소드인 punch
를 호출할 때마다 해당 객체의 hp
필드가 수정됨.
분석
- 객체 생성 시 체력 설정
thor
객체는 생성될 때 체력이 150으로 설정.thanos
객체는 생성될 때 체력이 160으로 설정.
punch
메소드 호출thor.punch(thanos);
호출 시,thanos
의 체력은 160에서 10 감소하여 150이 됨.- 다음
thor.punch(thanos);
호출 시,thanos
의 체력은 다시 10 감소하여 140이 됨. thanos.punch(thor);
호출 시,thor
의 체력은 150에서 10 감소하여 140이 됨.
상세 설명
thanos
의 체력이 160에서 “리셋”되지 않는 이유는, 프로그램에서thanos
객체의hp
필드가punch
메소드에 의해 계속 감소하기 때문이다. 객체의 필드 값은 메소드를 통해 변경될 때 이전 상태를 “기억”하고, 새로운 상태로 계속 유지됨.- 객체 지향 프로그래밍에서 객체의 필드(여기서는
hp
)는 클래스의 인스턴스가 살아있는 동안 계속 그 값을 유지함. 따라서, 한 번hp
가 감소하면, 그 변경된 값이 계속 객체에 반영된다. - 만약
thanos
의 체력을 특정 동작 후 원래 값(160)으로 리셋하고 싶다면, 직접hp
필드를 원하는 값으로 설정하는 로직을 추가해야 함.
예시: 체력 리셋 메소드 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Avenger {
String name;
int hp;
int originalHp; // 원래 체력을 저장하는 필드 추가
Avenger(String s, int i) {
name = s;
hp = i;
originalHp = i; // 생성자에서 원래 체력도 초기화
}
void punch(Avenger enemy) {
System.out.printf("[%s]의 펀치!", name);
enemy.hp -= 10;
System.out.printf(" -> %s의 체력 : %d\n", enemy.name, enemy.hp);
}
// 체력을 원래 값으로 리셋하는 메소드
void resetHp() {
hp = originalHp;
System.out.printf("%s의 체력이 %d로 리셋되었습니다.\n", name, hp);
}
}
이제 thanos.resetHp();
를 호출하면 thanos
의 hp
가 originalH p
(160)로 리셋됨.
static
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package today0509;
// 클래스 StudentTest의 정의 시작
public class StudentTest {
// main 메소드 시작: 프로그램의 시작점
public static void main(String[] args) {
// Student 클래스의 객체 park 생성, 학번은 201922104, 이름은 "Park"
Student park = new Student(201922104, "Park");
// Student 클래스의 객체 kim 생성, 학번은 201922104, 이름은 "Kim"
Student kim = new Student(201922104, "Kim");
// Student 클래스의 객체 lee 생성, 학번은 201922104, 이름은 "Lee"
Student lee = new Student(201922104, "Lee");
// Student 클래스의 static 변수 count를 사용하여 현재 생성된 Student 객체의 수를 출력
System.out.printf("Student 객체의 수 : %d", Student.count);
}
// main 메소드 끝
}
// Student 클래스의 정의 시작
class Student {
// 클래스 변수(static 필드), 모든 Student 객체가 공유하는 변수로, 생성된 Student 객체의 수를 저장
static int count = 0;
// 인스턴스 변수(non-static 필드), 각 Student 객체의 고유한 데이터를 저장
int id; // 학번을 저장하는 인스턴스 변수
String name; // 이름을 저장하는 인스턴스 변수
// 생성자 시작
// Student 객체를 생성하면서 학번(id)과 이름(name)을 초기화
Student(int _id, String _name) {
// Student 클래스의 static 변수 count를 1 증가시킴
Student.count++;
id = _id; // 인스턴스 변수 id를 매개변수 _id로 초기화
name = _name; // 인스턴스 변수 name을 매개변수 _name로 초기화
}
// 생성자 끝
}
// Student 클래스의 정의 끝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package today0509;
// MyMathTest 클래스 정의
public class MyMathTest {
// main 메소드 정의: 프로그램의 실행 시작점
public static void main(String[] args) {
// MyMath 클래스의 max 메소드를 호출하여 1.23과 3.45 중 더 큰 값을 출력
System.out.println(MyMath.max(1.23, 3.45));
// MyMath 클래스의 min 메소드를 호출하여 5.43과 3.21 중 더 작은 값을 출력
System.out.println(MyMath.min(5.43, 3.21));
}
}
// MyMath 클래스 정의
class MyMath {
// max 메소드 정의: 두 double 타입의 값을 비교하여 더 큰 값을 반환
static double max(double a, double b) {
// a와 b를 비교하여 더 큰 값(a 또는 b)을 반환
return (a > b) ? a : b;
}
// min 메소드 정의: 두 double 타입의 값을 비교하여 더 작은 값을 반환
static double min(double a, double b) {
// a와 b를 비교하여 더 작은 값(a 또는 b)을 반환
return (a < b) ? a : b;
}
}
접근제한자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package today0509;
// AccountTest 클래스 정의
public class AccountTest {
// main 메소드 정의: 프로그램의 실행 시작점
public static void main(String[] args) {
// Account 클래스의 객체 acc 생성
Account acc = new Account();
// acc 객체의 setBalance 메소드를 사용하여 잔액을 1000으로 설정
acc.setBalance(1000);
// acc 객체의 getBalance 메소드를 사용하여 잔액을 가져와 출력
System.out.printf("잔액 : %d", acc.getBalance());
}
}
// Account 클래스 정의
class Account {
// balance 필드(변수) 정의, private 접근 지정자를 사용하여 클래스 외부에서의 접근을 제한
private int balance;
// 게터
// getBalance 메소드 정의: balance 필드의 값을 반환하는 게터 메소드
public int getBalance() {
return balance;
}
// 세터
// setBalance 메소드 정의: balance 필드의 값을 설정하는 세터 메소드
public void setBalance(int b) {
// 매개변수로 받은 b 값을 balance 필드에 저장
balance = b;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package today0509;
// AccountTest 클래스 정의
public class AccountTest {
// main 메소드 정의: 프로그램의 실행 시작점
public static void main(String[] args) {
// Account 클래스의 객체 acc 생성
Account acc = new Account();
acc.balance = 10;
// acc 객체의 setBalance 메소드를 사용하여 잔액을 1000으로 설정
// acc.setBalance(1000);
// acc 객체의 getBalance 메소드를 사용하여 잔액을 가져와 출력
System.out.printf("잔액 : %d", acc.getBalance());
}
}
// Account 클래스 정의
class Account {
// balance 필드(변수) 정의, private 접근 지정자를 사용하여 클래스 외부에서의 접근을 제한
int balance;
// 게터
// getBalance 메소드 정의: balance 필드의 값을 반환하는 게터 메소드
public int getBalance() {
return balance;
}
// 세터
// setBalance 메소드 정의: balance 필드의 값을 설정하는 세터 메소드
public void setBalance(int b) {
// 매개변수로 받은 b 값을 balance 필드에 저장
balance = b;
}
}
private
접근 지정자를 사용해 필드(변수)를 선언하고 게터(getter)와 세터(setter) 메소드를 제공하는 방식은 객체 지향 프로그래밍에서 매우 중요한 설계 원칙인 캡슐화와 관련이 깊음.
캡슐화의 중요성
데이터 은닉과 보호:
private
키워드로 클래스의 필드를 선언하면, 그 필드는 클래스 외부에서 직접 접근할 수 없다. 이는 클래스의 데이터를 보호하고, 외부에서 예상치 못한 방식으로 그 데이터가 변경되는 것을 방지함. 데이터가 유효한 상태를 유지할 수 있도록 내부에서만 관리된다.인터페이스와 구현의 분리:
클래스를 사용하는 코드는 해당 클래스의 메소드를 통해서만 데이터에 접근할 수 있다. 이는 클래스의 내부 구현이 변경되더라도, 그 변경이 클래스를 사용하는 코드에 영향을 미치지 않도록 할 수 있다. 예를 들어, 필드에 대한 계산 방식이나 유효성 검사 로직이 바뀌어도, 게터와 세터의 시그니처(메소드 이름, 인자)가 바뀌지 않는 한 사용자 코드는 수정할 필요가 없다.
유효성 검사:
게터와 세터를 사용하면 데이터를 설정하거나 반환하기 전에 추가적인 로직을 실행할 수 있다. 예를 들어, 계좌 잔액(
balance
)이 절대 음수가 되지 않도록 세터에서 유효성 검사를 할 수 있다.추가 로직의 삽입:
게터와 세터 메소드를 통해 값을 설정하거나 가져올 때, 로깅(log 기록), 권한 검사, 캐싱 등 추가적인 기능을 쉽게 구현할 수 있다.
데이터 포맷팅:
게터에서는 클래스 사용자에게 데이터를 특정 포맷으로 제공할 수 있다. 예를 들어, 날짜 필드가 있을 때 내부적으로는
Timestamp
타입을 사용하지만, 이를 문자열로 포맷하여 반환할 수 있다.
예시
다음은 Account
클래스에서 balance
필드를 private
으로 선언하고, 게터와 세터를 통해 접근하는 예시. 세터에서는 음수 값을 설정하지 못하도록 유효성 검사를 추가한 모습.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Account {
private int balance; // balance는 클래스 외부에서 직접 접근할 수 없습니다.
// balance의 값을 반환하는 게터 메소드
public int getBalance() {
return balance;
}
// balance의 값을 설정하는 세터 메소드
public void setBalance(int b) {
if (b >= 0) { // balance에 음수가 설정되지 않도록 검사
balance = b;
} else {
System.out.println("잔액은 음수가 될 수 없습니다.");
}
}
}
문제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package today0509;
// CoffeeTest 클래스 정의
public class CoffeeTest {
// main 메소드 정의: 프로그램의 실행 시작점
public static void main(String[] args) {
// Coffee 클래스의 객체 c 생성, 커피 이름은 "아메리카노", 가격은 3000원
Coffee c = new Coffee("아메리카노", 3000);
// 객체 c의 getName 메소드와 getPrice 메소드를 호출하여 커피 이름과 가격을 출력
System.out.printf("%s(%d원) -> ", c.getName(), c.getPrice());
// 객체 c의 setPrice 메소드를 호출하여 커피 가격을 현재 가격에 500원 추가
c.setPrice(c.getPrice() + 500);
// 가격 변경 후, 객체 c의 getName 메소드와 getPrice 메소드를 호출하여 커피 이름과 가격을 출력
System.out.printf("%s(%d원)", c.getName(), c.getPrice());
}
}
// Coffee 클래스 정의
class Coffee {
// name 필드(변수) 정의, private 접근 지정자로 클래스 외부에서의 접근을 제한
private String name;
// price 필드(변수) 정의, private 접근 지정자로 클래스 외부에서의 접근을 제한
private int price;
// Coffee 클래스의 생성자
public Coffee(String n, int p) {
name = n; // 생성자의 매개변수 n을 name 필드에 할당
price = p; // 생성자의 매개변수 p를 price 필드에 할당
}
// name 필드의 값을 반환하는 게터 메소드
public String getName() {
return name;
}
// price 필드의 값을 반환하는 게터 메소드
public int getPrice() {
return price;
}
// price 필드의 값을 설정하는 세터 메소드
public void setPrice(int p) {
price = p; // 매개변수로 받은 p 값을 price 필드에 저장
}
}
자바 API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package today0509;
import java.util.Scanner; // Scanner 클래스를 사용하기 위해 java.util 패키지에서 Scanner를 가져옴
// UserInput 클래스 정의
public class UserInput {
// main 메소드 정의: 프로그램의 실행 시작점
public static void main(String[] args) {
// Scanner 객체 input 생성, 표준 입력 스트림(System.in)을 사용
Scanner input = new Scanner(System.in);
// 사용자에게 이름 입력을 요청
System.out.print("이름: ");
// 사용자로부터 이름을 입력받음
String name = input.next();
// 사용자에게 학번 입력을 요청
System.out.print("학번: ");
// 사용자로부터 학번을 입력받음, 정수로 처리
int number = input.nextInt();
// 사용자에게 학점 입력을 요청
System.out.print("학점: ");
// 사용자로부터 학점을 입력받음, 실수로 처리
double grade = input.nextDouble();
// 입력받은 이름, 학번, 학점을 포맷에 맞게 출력
System.out.printf("[%s]님의 학번은 %d이며, 학점은 %.2f 입니다.", name, number, grade);
ㅁ
// Scanner 객체 input을 닫음
input.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package today0509;
import java.lang.Math; // Math 클래스를 사용하기 위해 java.lang 패키지에서 Math를 가져옴
// MathTest 클래스 정의
public class MathTest {
// main 메소드 정의: 프로그램의 실행 시작점
public static void main(String[] args) {
// Math.PI를 사용하여 수학 상수 π(파이, 원주율)의 값을 출력
System.out.printf("수학의 파이(원주율) 값 : %f\n", Math.PI);
// Math.random()을 사용하여 0.0 이상 1.0 미만의 임의의 난수를 생성하여 출력
System.out.printf("임의 난수 값 : %f\n", Math.random());
// Math.floor(9.81)을 사용하여 9.81의 내림값을 계산하여 출력
System.out.printf("9.81의 내림값 : %f\n", Math.floor(9.81));
// Math.sqrt(4)을 사용하여 4의 제곱근을 계산하여 출력
System.out.printf("4의 제곱근 : %f\n", Math.sqrt(4));
// Math.pow(2, 3)을 사용하여 2의 3승을 계산하여 출력
System.out.printf("2의 3승 : %f\n", Math.pow(2, 3));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package today0509;
import java.util.*; // java.util 패키지에서 모든 클래스를 가져옴
// RandomTest 클래스 정의
public class RandomTest {
// main 메소드 정의: 프로그램의 실행 시작점
public static void main(String[] args) {
// Random 객체 random 생성
Random random = new Random();
// n을 10으로 초기화
int n = 10;
// random.nextInt()를 사용하여 임의의 정수 출력
// 첫 번째 출력은 형식 지정자(%d)를 사용하는 방식으로,
// 올바르게 출력되지 않으므로 수정이 필요합니다.
System.out.println("임의의 정수 : " + random.nextInt());
// 0 이상 n(10) 미만의 임의 정수 출력
System.out.printf("0이상 N(%d)미만의 임의 정수 : %d\n", n, random.nextInt(n));
// random.nextDouble()를 사용하여 0.0 이상 1.0 미만의 임의 실수 출력
System.out.printf("임의의 실수 : %f\n", random.nextDouble());
// random.nextBoolean()를 사용하여 임의의 참/거짓 값 출력
System.out.printf("임의의 참/거짓 : %b\n", random.nextBoolean());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package today0509;
import java.util.ArrayList; // ArrayList 클래스를 사용하기 위해 java.util 패키지에서 ArrayList를 가져옴
// ArrayListTest 클래스 정의
public class ArrayListTest {
// main 메소드 정의: 프로그램의 실행 시작점
public static void main(String[] args) {
// String 타입의 ArrayList 생성
ArrayList<String> names = new ArrayList<String>();
// names 리스트에 "Kim" 추가
names.add("Kim");
// names 리스트에 "Lee" 추가
names.add("Lee");
// names 리스트에 "Park" 추가
names.add("Park");
// names 리스트에 "Choi" 추가
names.add("Choi");
// names 리스트의 0번째 인덱스의 값을 "Han"으로 변경
names.set(0, "Han");
// names 리스트의 1번째 인덱스의 값을 제거하고, 제거된 값을 removed 변수에 저장
String removed = names.remove(1);
// names 리스트의 모든 요소를 출력
for (int i = 0; i < names.size(); i++) {
System.out.printf("%s ", names.get(i));
}
}
}
상속
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package today0509;
// RPGTest 클래스 정의
public class RPGTest {
// main 메소드 정의: 프로그램의 실행 시작점
public static void main(String[] args) {
// Wizard 클래스의 객체 wizard 생성
Wizard wizard = new Wizard();
// wizard 객체의 name 필드에 "간달프" 할당
wizard.name = "간달프";
// wizard 객체의 hp 필드에 100 할당
wizard.hp = 100;
// wizard 객체의 mp 필드에 80 할당
wizard.mp = 80;
// wizard 객체의 punch 메소드 호출
wizard.punch();
// wizard 객체의 fireball 메소드 호출
wizard.fireball();
}
}
// Novice 클래스 정의
class Novice {
// 초보자의 이름을 저장하는 필드
String name;
// 초보자의 체력을 저장하는 필드
int hp;
// punch 메소드 정의: 펀치를 날리는 행동을 출력
void punch() {
System.out.printf("%s(HP: %d)의 펀치!\n", name, hp);
}
}
// Wizard 클래스 정의: Novice 클래스를 상속받음
class Wizard extends Novice {
// 마법사의 마력을 저장하는 필드
int mp;
// fireball 메소드 정의: 파이어볼을 사용하는 행동을 출력
void fireball() {
System.out.printf("%s(HP: %d, MP : %d)의 파이어볼!\n", name, hp, mp);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package today0509;
// RPGTest 클래스 정의
public class RPGTest {
// main 메소드 정의: 프로그램의 실행 시작점
public static void main(String[] args) {
// Knight 클래스의 객체 knight 생성
Knight knight = new Knight();
// knight 객체의 name 필드에 "킹아서" 할당
knight.name = "킹아서";
// knight 객체의 hp 필드에 100 할당
knight.hp = 100;
// knight 객체의 stamina 필드에 70 할당
knight.stamina = 70;
// knight 객체의 punch 메소드 호출
knight.punch();
// knight 객체의 slash 메소드 호출
knight.slash();
}
}
// Novice 클래스 정의
class Novice {
// 초보자의 이름을 저장하는 필드
String name;
// 초보자의 체력을 저장하는 필드
int hp;
// punch 메소드 정의: 펀치를 날리는 행동을 출력
void punch() {
System.out.printf("%s(HP: %d)의 펀치!\n", name, hp);
}
}
// Wizard 클래스 정의: Novice 클래스를 상속받음
class Wizard extends Novice {
// 마법사의 마력을 저장하는 필드
int mp;
// fireball 메소드 정의: 파이어볼을 사용하는 행동을 출력
void fireball() {
System.out.printf("%s(HP: %d, MP : %d)의 파이어볼!\n", name, hp, mp);
}
}
// Knight 클래스 정의: Novice 클래스를 상속받음
class Knight extends Novice {
// 기사의 스태미나를 저장하는 필드
int stamina;
// slash 메소드 정의: 슬래쉬 공격을 사용하는 행동을 출력
void slash() {
System.out.printf("%s(HP: %d, ST : %d)의 슬래쉬!\n", name, hp, stamina);
}
}
의문
- 어떻게 main 메서드를 찾는걸까? : main 메타데이터를 가지고 있음. 한 줄씩 main을 찾는게 아님
- 왜 String만 대문자로 double이나 int는 소문자일까? :
String은 객체.
Java에서 String은 불변(immutable) 객체로, 한 번 생성되면 그 내용을 변경할 수 없다.- 리터럴 vs.
new
키워드String s1 = "Hello";
와 같이 리터럴 방식으로 String을 생성하면, JVM은 “Hello”라는 값을 String Pool 내에 저장하고, 동일한 리터럴 값으로 선언된 모든 String 변수는 메모리 내 같은 객체를 참조하게 된다. 따라서s1
과s2
는 같은 메모리 주소를 참조.String s3 = new String("Hello");
는 매번 새로운 String 객체를 메모리에 생성. 따라서s3
과s4
는 내용은 같지만, 서로 다른 메모리 주소를 가지게 됨.
기본 타입(primitive type).
int
**는 Java의 기본 자료형 중 하나로, 메모리에 직접 값을 저장하며 객체가 아니다.값에 의한 비교
int
타입의 변수들은 그들의 값에 의해 직접 비교됨. 예를 들어, 두int
변수가 같은 값을 가지고 있으면, 그들은 같다고 평가함.==
vs.equals()
==
연산자: 주소 값을 비교. 따라서, 두 String 변수가 같은 객체를 참조하고 있는지 확인한다.equals()
메소드: 내용 자체를 비교.String
클래스에서equals()
메소드는 두 문자열의 내용이 같은지 비교하도록 오버라이드되어 있다.요약
String s1 = "Hello";
와String s2 = "Hello";
는 같은 String Pool 내 객체를 참조하므로s1 == s2
는true
이다.String s3 = new String("Hello");
와String s4 = new String("Hello");
는 내용은 같지만, 서로 다른 객체를 참조하므로s3 == s4
는false
이다. 하지만s3.equals(s4)
는true
이다, 왜냐하면equals()
는 값의 내용을 비교하기 때문임.
이렇게 Java에서는 객체와 기본 타입을 다르게 처리하며, String의 경우 특별한 메모리 관리 방식을 통해 효율성을 높이고 있음.