#1 교육정리
1) String
#2 String
String 클래스에는 문자열과 관련된 작업을 할 때 유용하게 사용할 수 있는 다양한 메소드가 포함되어 있습니다.
이러한 String 클래스는 java.lang 패키지에 포함되어 제공됩니다.
String str1 = new String("abc"); // 생성자의 매개변수로 문자열 생성
String str2 = "test"; // 문자열 상수를 가리키는 방식
언뜻 비슷해 보이지만, 내부적으로 두가지 방식은 큰 차이가 있습니다. new 예약어를 사요ㅕㅇ하여 객체를 생성하는 경우는 "abc" 문자열을 위한 메모리가 할당되고 새로운 객체가 생성됩니다.
하지만 str2 = "test" 와 같이 생성자를 이용하지 않고 바로 문자열 상수를 가리키는 경우에는 str2가 기존에 만들어져 있던 "test"라는 문자열 상수의 메모리 주소를 가리키게 됩니다.
따라서 String str3 = "test" 코드를 작성하면 str2와 str3는 주소 값이 같게 됩니다.
그림으로 보면 다음과 같습니다.
String 인스턴스는 한 번 생성되면 그 값을 읽기만 할 수 있고, 변경할 수는 없습니다.
이러한 객체를 자바에서는 불변 객체(immutable object)라고 합니다.
즉, 자바에서 덧셈(+) 연산자를 이용하여 문자열 결합을 수행하면, 기존 문자열의 내용이 변경되는 것이 아니라 내용이 합쳐진 새로운 String 인스턴스가 생성되는 것입니다.
String 문자열 객체 만들기
// String - 문자열 객체 만들기
package com.eomcs.basic.ex02;
public class Exam0110 {
public static void main(String[] args) {
// String 레퍼런스
// - String은 자바 기본 타입이 아니다.
// - 클래스이다.
String s1; // s1은 String 인스턴스 주소를 담는 레퍼런스이다.
// String 인스턴스
// - 힙에 Hello 문자 코드를 저장할 메모리를 만들고 그 주소를 리턴한다.
// - 내용물의 동일 여부를 검사하지 않고 무조건 인스턴스를 생성한다.
// - 가비지가 되면 가비지 컬렉터에 의해 제거된다.
s1 = new String("Hello");
String s2 = new String("Hello");
// 인스턴스가 같은지를 비교해보면,
System.out.println(s1 == s2); // false => 서로 다른 인스턴스이다.
}
}
false
문자열 리터럴
리터럴(literal) 표기라고 하는데 객체 생성없이 고정된 값을 그대로 대입하는 방법을 말한다.
// String - 문자열 리터럴
package com.eomcs.basic.ex02;
public class Exam0111 {
public static void main(String[] args) {
// 문자열 리터럴
// - string constant pool 메모리 영역에 String 인스턴스를 생성한다.
// - 상수풀에 이미 같은 문자열의 인스턴스가 있다면, 그 주소를 리턴한다.
// - 왜? 메모리 절약을 위해 중복 데이터를 갖는 인스턴스를 생성하지 않는다.
// - JVM이 끝날 때까지 메모리에 유지된다.
//
String x1 = "Hello"; // 새 String 인스턴스의 주소를 리턴한다.
String x2 = "Hello"; // 기존의 String 인스턴스 주소를 리턴한다.
// 인스턴스의 주소를 비교해 보면,
System.out.println(x1 == x2); // true => 두 String 객체는 같다.
}
}
true
String 인스턴스를 상수풀에 생성하기: intern()
- String 객체에 들어 있는 문자열과 동일한 문자열을 갖고 있는 String 객체를 상수풀에서 찾는다.
- 있으면 그 String 객체의 주소를 리턴한다.
- 없으면 상수풀에 String 객체를 생성한 후 그 주소를 리턴한다.
// String - String 인스턴스를 상수풀에 생성하기: intern()
package com.eomcs.basic.ex02;
public class Exam0113 {
public static void main(String[] args) {
String s1 = new String("Hello"); // Heap 영역에 String 인스턴스를 생성한다.
String s2 = s1.intern();
String s3 = "Hello"; // 해당 문자열을 가진 String 객체를 String Pool에서 찾는다.
// 있으면 그 객체를 리턴한다. 없으면 새 객체를 만들고 리턴한다.
System.out.println(s1 == s2);
System.out.println(s2 == s3);
}
}
false
true
String 인스터스에 있는 값을 비교하기 : equals()
equals는 두개의 문자열이 동일한지를 비교하여 결과값을 리턴한다.
문자열의 값을 비교할때는 반드시 equals 를 사용해야 한다. 추가적으로 == 연산자를 사용할 경우, == 은 두개의 자료형이 동일한 객체인지를 판별할 때 사용하는 연산자이기 때문에 false를 리턴한다.
// String - String 인스터스에 있는 값을 비교하기 : equals()
package com.eomcs.basic.ex02;
public class Exam0120 {
public static void main(String[] args) {
String s1 = new String("Hello");
String s2 = new String("Hello");
// 두 String 인스턴스는 분명히 서로 다르다.
System.out.println(s1 == s2);
// 두 인스턴스가 갖고 있는 문자열이 같은지를 비교하고 싶다면,
System.out.println(s1.equals(s2));
// equals()?
// - Object에 정의되어 있는 메서드이다.
// - 인스턴스가 같은지 비교한다.
//
// String의 equals()?
// - Object에서 상속 받은 것을 오버라이딩하였다.
// - 문자열이 같은지 비교한다.
//
}
}
false
true
String 인스터스에 있는 값을 비교하기 : equalsIgnoreCase()
대소문자 구분없이 문자열의 값을 비교하고 싶다면 equalsIgnoreCase()를 사용하면 됩니다.
// String - String 인스터스에 있는 값을 비교하기 : equalsIgnoreCase()
package com.eomcs.basic.ex02;
public class Exam0121 {
public static void main(String[] args) {
String s1 = new String("Hello");
String s2 = new String("HELLO");
// equals()는 대소문자를 구분한다.
System.out.println(s1.equals(s2));
// 대소문자 구분없이 문자열을 비교하고 싶다면,
System.out.println(s1.equalsIgnoreCase(s2));
}
}
false
true
String.equals() vs Object.equals()
equals() 를 오버라이딩 하지 않으면, 단순히 인스턴스가 같은지만 비교합니다.
// String - String.equals() vs Object.equals()
package com.eomcs.basic.ex02;
public class Exam0122 {
static class Member {
String name;
int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
}
public static void main(String[] args) {
Member m1 = new Member("홍길동", 20);
Member m2 = new Member("홍길동", 20);
// Member 인스턴스를 통해 호출하는 메서드는 모두
// Object 클래스의 메서드이다.
m1.toString();
m1.hashCode();
m1.equals(null);
// 비록 m1과 m2는 같은 값을 갖고 있지만 인스턴스가 다르다.
System.out.println(m1 == m2); // false
// Member는 Object에서 상속 받은 equals()를 오버라이딩 하지 않았다.
// 따라서 단순히 인스턴스가 같은지를 비교할 것이다.
System.out.println(m1.equals(m2)); // false
System.out.println(m1.toString());
System.out.println(m2.toString());
}
}
false
false
$Member@251a69d7
$Member@7344699f
StringBuffer - 문자열 비교
StringBuffer나 StringBuilder의 경우 문자열 데이터를 다룬다는 점에서 String 객체와 같지만, 객체의 공간이 부족해지는 경우 버퍼의 크기를 유연하게 늘려주어 가변(mutable)적이라는 차이점이 있다.
내부 Buffer(데이터를 임시로 저장하는 메모리)에 문자열을 저장해두고 그 안에서 추가, 수정, 삭제 작업을 할 수 있도록 설계되어 있다.
String 객체는 한번 생성되면 불변적인 특징 때문에 값을 업데이트하면, 매 연산 시마다 새로운 문자열을 가진 String 인스턴스가 생성되어 메모리공간을 차지하게 되지만,
StringBuffer / StringBuilder 는 가변성 가지기 때문에 .append() .delete() 등의 API를 이용하여 동일 객체내에서 문자열 크기를 변경하는 것이 가능하다.
// StringBuffer - 문자열 비교
package com.eomcs.basic.ex02;
public class Exam0124 {
public static void main(String[] args) {
StringBuffer b1 = new StringBuffer("Hello");
StringBuffer b2 = new StringBuffer("Hello");
// b1, b2는 서로 다른 인스턴스이다.
System.out.println(b1 == b2); // false
// StringBuffer는 Object에서 상속 받은 equals()를 오버라이딩 하지 않았다.
System.out.println(b1.equals(b2)); // false
// => 따라서 원래 Object의 equals()를 사용한다.
// => 즉 두 개의 인스턴스가 같은지를 비교한다.
// 그러니 결과는 false이다.
//
// 어떤 클래스에 equals()가 있나요?
// => 모든 클래스에 있다.
//
// 엥?
// => 자바의 모든 클래스는 Object 클래스에 정의된 기능을 상속 받는다.
// Object를 상속 받지 않을 수 없다.
// => 따라서 자바의 모든 클래스는 Object 클래스에 정의된 메서드를 사용할 수 있다.
//
// Object 클래스에 어떤 메서드가 있나요?
// => equals() : 인스턴스가 같은지를 비교한다.
// => toString() : 인스턴스의 클래스명과 해시코드를 리턴한다.
// => hashCode() : 인스턴스의 해시 코드를 리턴한다.
// => getClass() : 클래스 정보를 리턴한다.
// => clone() : 인스턴스를 복제하여 리턴한다.
// => finalize() : 가비지 컬렉터에 의해 제거되기 전에 호출된다.
// => wait() : 잠시 실행을 멈춘다. 외부에서 깨워줄 때까지 멈춘다.
// => notify() : wait()로 실행을 멈춘 것을 깨운다.
// => notifyAll() : wait()로 실행을 멈춘 것을 모두 깨운다.
// Object로부터 상속 받은 equals()가 원래는 인스턴스가 같은지를 비교한다고 했는데,
// 왜, String 클래스는 인스턴스가 아닌 문자열 값을 비교하나요?
// => String 클래스는 상속 받은 equals() 메서드를 재정의하였기 때문이다.
// => 이렇게 상속 받은 기능을 자신의 역할에 맞게끔 재정의하는 것을
// "오버라이딩(overriding)"이라 한다.
//
// StringBuffer는 재정의 하지 않아서 다르다고 결과가 나온건가요?
// => 예!
}
}
false
false
StringBuffer - 문자열 비교2
문자열을 비교하려면 아래와 같이 toString()을 사용하여 꺼내서 비교하면 됩니다.
// StringBuffer - 문자열 비교 II
package com.eomcs.basic.ex02;
public class Exam0125 {
public static void main(String[] args) {
StringBuffer b1 = new StringBuffer("Hello");
StringBuffer b2 = new StringBuffer("Hello");
// StringBuffer 에 들어 있는 문자열을 비교하려면?
// - StringBuffer에서 String을 꺼내 비교하라!
//
// String s1 = b1.toString();
// String s2 = b2.toString();
// System.out.println(s1.equals(s2));
//
System.out.println(b1.toString().equals(b2.toString()));
}
}
true
hashcode() 메서드)
해시(hash)는 정보를 저장하거나 검색할 때 사용하는 자료 구조입니다. 정보를 어디에 저장할 것인지, 어디서 가져올 것인지 해시 함수를 사용하여 구현합니다. 해시 함수는 객체의 특정정보(키 값)를 매개변수 값으로 넣으면 그 객체가 저장되어야 할 위치나 저장된 해시 테이블 주소(위치)를 반환합니다.
즉, 객체를 식별할 수 있는 유니크한 값을 말한다. 메모리에 생성된 객체의 주소를 정수로 변환한 형태를 얘기하는데, 이 정수는 중복되지 않는 고유의 값입니다.
// String - hashCode()
package com.eomcs.basic.ex02;
public class Exam0130 {
static class Member {
String name;
int age;
public Member(String n, int a) {
name = n;
age = a;
}
}
public static void main(String[] args) {
Member m1 = new Member("aaa", 20);
Member m2 = new Member("aaa", 20);
// 인스턴스가 다르다.
System.out.println(m1 == m2);
// Object의 hashCode()는 인스턴스 마다 다르다.
System.out.println(m1.hashCode() == m2.hashCode());
System.out.println("------------------------------");
String s1 = new String("Hello");
String s2 = new String("Hello");
// 두 String 의 인스턴스가 다르다.
System.out.println(s1 == s2);
// String의 hashCode()?
System.out.println(s1.hashCode() == s2.hashCode()); // true
//
// 그러나, String의 hashCode()은
// 문자열이 같으면 같은 hashCode()를 리턴하도록 오버라이딩 하였다.
// 이유?
// - 문자열이 같은 경우 같은 객체로 다루기 위함이다.
// - HashSet 에서 객체를 저장할 때 이 메서드의 리턴 값으로 저장 위치를 계산한다.
// - HashMap이나 Hashtable에서는 Key를 다룰 때 이 메서드의 리턴 값을 사용한다.
// - 보통 hashCode()와 equals()를 함께 오버라이딩 한다.
// String 클래스는 toString()도 오버라이딩 했다.
System.out.println(s1.toString());
}
}
false
false
------------------------------
false
true
Hello
toString(): 다형적 변수와 형변환
Object.toString()은 "클래스명@해시값" 을 리턴한다.
String은 상속 받은 toString()을 오버라이딩 했다.
=> this 주소를 그대로 리턴한다.
// String - toString(): 다형적 변수와 형변환
package com.eomcs.basic.ex02;
public class Exam0141 {
public static void main(String[] args) {
Object obj = new String("Hello"); // 인스턴스 주소가 100이라 가정하자;
String x1 = (String) obj; // x1 <--- 100
System.out.println(obj == x1);
// obj에 대해 toString()을 호출할 때,
// => 일단 obj 클래스에 선언된 멤버(필드와 메서드)만 사용할 수 있다.
// => 단 멤버는 실제 obj가 가리키는 클래스부터 찾아 올라 간다.
// => 위 예에서 obj가 가리키는 것은 String 이기 때문에
// => 이 경우 toString()을 호출할 때 String 클래스에서부터 찾는다.
// => String 클래스가 toString()을 오버라이딩 했기 때문에
// 결국 이 오버라이딩 메서드를 호출할 것이다.
String x2 = obj.toString(); // x2 <---- 100
System.out.println(x1 == x2);
// 레퍼런스를 통해 메서드를 호출할 때
// => 레퍼런스가 가리키는 객체의 클래스부터 메서드를 찾아 올라간다.
// => 따라서 obj가 가리키는 객체의 클래스가 String이기 때문에
// obj.toString()은 String 클래스부터 해당 메서드를 찾는다.
}
}
true
true
equals(), hashCode() 의 관계)
// String - equals(), hashCode()
package com.eomcs.basic.ex02;
public class Exam0150 {
static class Member {
String name;
int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
}
public static void main(String[] args) {
Member m1 = new Member("홍길동", 20);
Member m2 = new Member("홍길동", 20);
// 비록 m1과 m2는 같은 값을 갖고 있지만 인스턴스가 다르다.
System.out.println(m1 == m2); // false
// Object로부터 상속 받은 hashCode()는 인스턴스 마다 고유의 해시값을 리턴한다.
System.out.printf("%x, %x\n", m1.hashCode(), m2.hashCode());
// Object로부터 상속 받은 toString()은 "클래스명@해시값"을 리턴한다.
System.out.printf("%s, %s\n", m1.toString(), m2.toString());
// Object로부터 상속 받은 equals()는 인스턴스가 같은지 검사한다.
System.out.printf("%b\n", m1.equals(m2)); // false
System.out.println("-------------------------------------");
String s1 = new String("Hello");
String s2 = new String("Hello");
// 비록 s1과 s2는 같은 문자열을 갖고 있지만 인스턴스가 다르다.
System.out.println(s1 == s2);
// String 클래스는 Object로부터 상속 받은 hashCode()를 오버라이딩 했다.
// => 문자열이 같으면 같은 해시값을 리턴하게 만들었다.
System.out.printf("%x, %x\n", s1.hashCode(), s2.hashCode());
// String 클래스는 Object로부터 상속 받은 toString()을 오버라이딩 했다.
// => 스트링 인스턴스에 보관된 문자열을 리턴하도록 만들었다.
System.out.printf("%s, %s\n", s1.toString(), s2.toString());
// String 클래스는 Object로부터 상속 받은 equals()를 오버라이딩 했다.
// => 문자열이 같은지를 비교하도록 만들었다.
System.out.printf("%b\n", s1.equals(s2));
}
}
false
251a69d7, 7344699f
com.eomcs.basic.ex02.Exam0150$Member@251a69d7, com.eomcs.basic.ex02.Exam0150$Member@7344699f
false
-------------------------------------
false
42628b2, 42628b2
Hello,
mutable vs immutable 객체)
String 객체는 불변객체로 한 번 객체에 값을 담으면 변경할 수 없습니다.
String 클래스의 메서드는 원본 인스턴스의 데이터를 변경하지 않고 새로 String 객체를 만들 뿐입니다.
// String - mutable vs immutable 객체
package com.eomcs.basic.ex02;
public class Exam0160 {
public static void main(String[] args) {
// String 객체는 immutable 객체이다.
// 즉 한 번 객체에 값을 담으면 변경할 수 없다.
String s1 = new String("Hello");
// String 클래스의 메서드는 원본 인스턴스의 데이터를 변경하지 않는다.
// 다만 새로 String 객체를 만들 뿐이다.
String s2 = s1.replace('l', 'x');
System.out.println(s1 == s2);
System.out.printf("%s : %s\n", s1, s2); // 원본은 바뀌지 않는다.
String s3 = s1.concat(", world!");
System.out.printf("%s : %s\n", s1, s3); // 원본은 바뀌지 않는다.
}
}
false
Hello : Hexxo
Hello : Hello, world!
기타 메서드 : Arrays.copyOfRange()
// String - 기타 메서드 : Arrays.copyOfRange()
package com.eomcs.basic.ex02;
import java.util.Arrays;
public class Exam0172 {
public static void main(String[] args) {
String[] arr = {"101", "제목", "내용", "4", "2021-2-2"};
// 배열에서 특정 범위의 항목을 복사하기
String[] arr2 = Arrays.copyOfRange(arr, 2, 4);
for (String s : arr2) {
System.out.println(s);
}
}
}
내용
4
다양한 생성자 활용)
// String - 다양한 생성자 활용
package com.eomcs.basic.ex02;
public class Exam0180 {
public static void main(String[] args) throws Exception {
// String 인스턴스 생성
String s1 = new String();
// => 내부적으로 문자의 코드 값을 저장할 char 배열(버전 1.8 까지)
// 또는 byte 배열(버전 1.9 부터)을 생성한다.
// => 생성자에서 넘겨주는 값을 배열에 저장한다.
// => 만약 생성자에 아무것도 넘겨주지 않으면 빈 배열이 생성된다.
System.out.println(s1); // 빈 문자열 출력
String s2 = new String("Hello"); // 문자열 리터럴로 String 인스턴스 생성
System.out.printf("s2=%s\n", s2);
char[] chars = {'H', 'e', 'l', 'l', 'o'};
String s3 = new String(chars); // char 배열로 String 인스턴스 생성
System.out.printf("s3=%s\n", s3);
byte[] bytes =
{(byte) 0xb0, (byte) 0xa1, (byte) 0xb0, (byte) 0xa2, 0x30, 0x31, 0x32, 0x41, 0x42, 0x43};
// 문자 코드 값이 저장된 바이트 배열로 String 인스턴스 생성
String s4 = new String(bytes);
System.out.printf("s4=%s\n", s4);
// 한글이 깨진다. 이유?
// => String 생성자는 파라미터로 받은 바이트 배열에 ISO-8859-1 문자 코드가 들어 있다가 간주한다.
// 즉 0xb0 0xa1 값이 한글 '가'가 아니라 0xb0와 0xa1 각각을 영어라 간주하고
// ISO-8859-1 에 정의된 문자표에 따라 유니코드(UTF-16)으로 바꿔 저장한다.
// 0xb0(ISO-8859-1) ==> 0x00b0(UTF-16)
// => 제대로 한글을 처리하려면?
// 생성자에 바이트 배열을 넘겨줄 때
// 배열에 들어 있는 코드 값이 어떤 문자표의 코드 값인지 알려줘야 한다.
//
String s5 = new String(bytes, "euc-kr");
System.out.printf("s5=%s\n", s5);
byte[] bytes2 =
{(byte) 0xac, (byte) 0x00, (byte) 0xac, (byte) 0x01, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63};
// 바이트 배열에 들어 있는 코드는 무슨 문자표로 작성했는지 정확하게 알려줘야 한다.
// 그래야 자바의 문자 코드로 제대로 변경할 수 있을 것이다.
String s6 = new String(bytes2, "utf-16");
System.out.printf("s6=%s\n", s6);
byte[] bytes3 = {(byte) 0xea, (byte) 0xb0, (byte) 0x80, (byte) 0xea, (byte) 0xb0, (byte) 0x81,
0x61, 0x62, 0x63};
String s7 = new String(bytes3, "utf-8");
System.out.printf("s7=%s\n", s7);
}
}
s2=Hello
s3=Hello
s4=����012ABC
s5=가각012ABC
s6=가각abc
s7=가각abc
Wrapper 클래스)
클래스를 기본 자료형을을 감쌋다는 의미로 Wrapper 클래스라고 합니다.
// Wrapper 클래스 - 종류
package com.eomcs.basic.ex02;
public class Exam0210 {
public static void main(String[] args) {
// 자바는 primitive type의 data 를 객체처럼 다룰 수 있도록
// 각 타입에 대응하는 클래스를 제공한다.
// 예)
Byte b = new Byte((byte)100); // ==> byte
Short s = new Short((short)20000); // ==> short
Integer i = new Integer(3000000); // ==> int
Long l = new Long(60000000000L); // ==> long
Float f = new Float(3.14f); // ==> float
Double d = new Double(3.14159); // ==> double
Boolean bool = new Boolean(true); // ==> boolean
Character c = new Character((char)0x41); // ==> char
// 이렇게 primitive data type에 대응하여 만든 클래스를
// primitive data를 포장하는 객체라고 해서
// "랩퍼(wrapper) 클래스"라 부른다.
// 래퍼 클래스의 주요 용도:
// => primitive data type의 값을 객체로 주고 받을 때 사용한다.
// => primitive data type의 값을 객체에 담아 전달하고 싶다면
// 언제든 wrapper 클래스의 인스턴스를 만들면 된다.
//
// Java 9 부터 wrapper 클래스의 생성자가 deprecated 상태이다.
// 가능한 생성자를 사용하여 인스턴스를 생성하지 말라!
// deprecated(비난받는, 유지보수가 중단되어, 사용이 권장되지 않는)?
// - 사용하지 않는 것이 좋다고 결정되었고, 가까운 장래에 제거될 것이라는 의미.
//
// Wrapper 클래스의 인스턴스를 생성할 때는 생성자 대신 클래스 메서드를 사용하라.
Byte b2 = Byte.valueOf((byte)100);
Short s2 = Short.valueOf((short)20000);
Integer i2 = Integer.valueOf(3000000);
Long l2 = Long.valueOf(60000000000L);
Float f2 = Float.valueOf(3.14f);
Double d2 = Double.valueOf(3.14159);
Boolean bool2 = Boolean.valueOf(true);
Character c2 = Character.valueOf((char)0x41);
}
}
primitive type을 객체로 다루기)
// Wrapper 클래스 - primitive type 을 객체로 다루기
package com.eomcs.basic.ex02;
public class Exam0211 {
public static void main(String[] args) {
// Wrapper 클래스의 가장 큰 목적!
// => primitive 값을 포함하여 모든 값을 쉽게 주고 받기 위함이다.
// wapper 클래스가 없다면,
// 다음과 같이 각 타입에 대한 메서드가 따로 있어야 한다.
long l = 100L;
double d = 3.14;
boolean bool = true;
m(l);
m(d);
m(bool);
// 이렇게 wrapper 클래스를 사용하면
// 객체로 다룰 수 있다.
Long obj1 = Long.valueOf(l);
Double obj2 = Double.valueOf(d);
Boolean obj3 = Boolean.valueOf(bool);
m(obj1);
m(obj2);
m(obj3);
}
// 만약에 Wrapper 클래스가 없다면 다음과 같이
// 정수를 받는 메서드와 부동소수점을 받는 메서드,
// 논리값을 받는 메서드를 따로따로 정의해야 한다.
// => 이런 불편함을 없애기 위해 Wrapper 클래스를 만든 것이다.
// => 즉 primitive type을 객체처럼 다룰 수 있도록 만든 문법이다.
static void m(long value) { // byte, short, int, long, char
System.out.printf("long value=%s\n", value);
}
static void m(double value) {// float, double
System.out.printf("double value=%s\n", value);
}
static void m(boolean value) {// boolean
System.out.printf("boolean value=%s\n", value);
}
// wapper 클래스는 primitive type의 값을 객체로 다룰 수 있게 해준다.
// primitive type에 상관없이 Object 타입의 파라미터로 값을 받을 수 있다.
static void m(Object value) { // 모든 객체를 받을 수 있다.
System.out.printf("wrapper value=%s\n", value);
}
}
long value=100
double value=3.14
boolean value=true
wrapper value=100
wrapper value=3.14
wrapper value=true
오토박싱(auto-boxing)/오토언박싱(auto-unboxing)
다음과 같이 프로그래밍 중에 primitive type의 값을 인스턴스에 담고("박싱(boxing)"이라 부른다)
인스턴스의 담긴 primitive 값을 다시 꺼내는 일("언박싱(unboxing)"이라 부른다)은 매우 불편합니다.
즉, 기본형을 객체형으로 바꾸는 것을 오토박싱, 객체형을 기본형으로 꺼내는 것을 언박싱 이라고 합니다.
이는 자바의 연산 방식이 변경된 것이 아니라 컴파일러가 변경하는 것입니다. 따라서 객체의 형 변환에 신경 쓰지 않고 편리하게 프로그래밍 할 수 있습니다.
// Wrapper 클래스 - 오토박싱(auto-boxing)/오토언박싱(auto-unboxing)
package com.eomcs.basic.ex02;
public class Exam0220 {
public static void main(String[] args) {
// int ==> Integer
int i1 = 100;
Integer obj1 = Integer.valueOf(i1);
// Integer ==> int
Integer obj2 = Integer.valueOf(200);
int i2 = obj2.intValue();
}
}
obj는 레퍼런스인데 오토박싱이 어떻게 가능할까요?
// Wrapper 클래스 - 오토박싱(auto-boxing)/오토언박싱(auto-unboxing)
package com.eomcs.basic.ex02;
public class Exam0221 {
public static void main(String[] args) {
// 오토박싱
// - Java 1.5부터 지원하는 문법이다.
// - 코드의 문맥에 따라 박싱(boxing)과 언박싱(unboxing)을 자동으로 수행한다.
//
// 즉 primitive data type 값을 Wrapper 클래스의 인스턴스에 바로 할당할 수 있다.
//
Integer obj = 100; // ==> Integer.valueOf(100)
// obj는 레퍼런스인데 어떻게 가능한가?
// => 내부적으로 Integer.valueOf(100) 호출 코드로 바뀐다.
// => 즉 int 값이 obj에 바로 저장되는 것이 아니라,
// 내부적으로 Integer 객체가 생성되어 그 주소가 저장된다.
// => 이렇게 int 값을 자동으로 Integer 객체로 만드는 것을
// "오토박싱(auto-boxing)"이라 한다.
}
}
obj에 저장된 것은 int 값이 아니라 Integer 객체의 주소인데 어떻게 가능할까요?
// Wrapper 클래스 - 오토박싱(auto-boxing)/오토언박싱(auto-unboxing)
package com.eomcs.basic.ex02;
public class Exam0222 {
public static void main(String[] args) {
// 오토 언박싱
// - Java 1.5부터 지원하는 문법이다.
// - 코드의 문맥에 따라 박싱(boxing)과 언박싱(unboxing)을 자동으로 수행한다.
//
// 즉 Wrapper 객체의 값을 primitive data type 변수에 직접 할당할 수 있다.
//
Integer obj = Integer.valueOf(300);
int i = obj; // ==> obj.intValue()
// obj에 저장된 것은 int 값이 아니라 Integer 객체의 주소인데 어떻게 가능한가?
// => 내부적으로 obj.intValue() 호출 코드로 바뀐다.
// => 즉 obj에 들어있는 인스턴스 주소가 i에 저장되는 것이 아니라,
// obj 인스턴스에 들어 있는 값을 꺼내 i에 저장하는 것이다.
// => 이렇게 Wrapper 객체 안에 들어 있는 값을 자동으로 꺼낸다고 해서
// "오토언박싱"이라 부른다.
}
}
wrapper 객체 생성)
// Wrapper 클래스 - wrapper 객체 생성
package com.eomcs.basic.ex02;
public class Exam0230 {
public static void main(String[] args) {
// new 명령을 사용하여 Integer 객체를 만들면
// 무조건 새 인스턴스를 생성한다.
Integer obj1 = new Integer(100); // Heap에 인스턴스 생성
Integer obj2 = new Integer(100); // Heap에 인스턴스 생성
System.out.println(obj1 == obj2); // false
// 설명:
// => new 연산자를 통해 Integer 객체를 생성하면 Heap에 인스턴스를 생성한다.
// => 그래서 같은 값이더라도 다른 인스턴스가 생성된다.
// auto-boxing 으로 Wrapper 객체를 생성할 경우,
Integer obj3 = 100; // Integer.valueOf(100)
Integer obj4 = 100; // Integer.valueOf(100);
System.out.println(obj3 == obj4); // true
// 설명:
// => 정수 값이 -128 ~ 127 범위일 경우
// 자주 사용되는 수이기 때문에
// String 리터럴처럼 상수 풀에 Integer 객체를 생성한다.
// => 메모리를 효율적으로 사용하기 위해
// 같은 값을 가지는 Integer 객체가 여러 개 존재하지 않게 한다.
// => 그래서 가능한 이 방법을 사용해야 한다.
// auto-boxing은 Wrapper 클래스의 valueOf()를 호출하는 코드로 처리된다.
Integer obj5 = Integer.valueOf(100);
Integer obj6 = Integer.valueOf(100);
System.out.println(obj5 == obj6); // true
// 다음과 같이 auto-boxing으로 생성된 객체와 valueOf()가 리턴한 객체를 비교해 보자!
System.out.println(obj3 == obj5); // true
// 주의!
// -128 ~ 127 범위를 넘어가는 경우 무조건 새 객체를 만든다.
// 이유?
// - 다루는 숫자가 너무 많기 때문에 무조건 상수 풀에 만들기에는
// 오히려 메모리 낭비가 심해지기 때문이다.
// 상수풀에 생성된 객체는 JVM이 종료되기 전까지 유지된다.
// 즉 가비지가 되지 않는다.
// - 그러나 heap에 생성된 객체는 주소를 잃어 버리면 가비지가 되기 때문에
// 메모리를 좀 더 효율적으로 사용할 수 있다.
//
Integer obj7 = 128;
Integer obj8 = 128;
Integer obj9 = 128;
System.out.println(obj7 == obj8); // false
System.out.println(obj7 == obj9); // false
System.out.println(obj8 == obj9); // false
// 따라서 이렇게 생성된 wrapper 객체의 값을 비교할 때는
// String 처럼 equals()로 비교하라!
System.out.println(obj7.equals(obj8));
// 결론!
// - wrapper 객체의 값을 비교할 때 == 연산자를 사용하지 말라!
// - -128 ~ 127 범위 내의 값이라면 == 연산자를 사용하여 비교할 수도 있지만,
// 매번 비교할 때 마다 범위의 유효성을 생각하는 것이 번거롭다.
// - 그냥 equals() 메서드를 사용하여 값을 비교하라!
// - 더 좋은 방법은 auto-unboxing 하여 primitive type 의 값으로 바꾼후에 == 연산자로 비교하라.
// 예시) System.out.println(obj7.intValue() == obj8.intValue());
}
}
false
true
true
true
false
false
false
true
wrapper 객체의 값 비교)
// Wrapper 클래스 - wrapper 객체의 값 비교
package com.eomcs.basic.ex02;
public class Exam0231 {
public static void main(String[] args) {
Integer obj1 = Integer.valueOf(100);
Integer obj2 = 100;
System.out.println(obj1 == obj2);
// auto-boxing이나 valueOf()로 생성한 wrapper 객체는
// constants pool에 오직 한 개만 생성되기 때문에
// 값을 비교할 때 그냥 == 연산자를 사용하여 인스턴스 주소로 비교해도 된다.
// 단, -128 ~ 127 범위의 값에 대해서만 적용된다.
// 그러나 다음과 같이 new 연산자로 만든 wrapper 객체는
// 값이 같더라도 인스턴스가 따로 생성되기 때문에,
Integer obj3 = new Integer(100); // Heap에 인스턴스 생성
Integer obj4 = new Integer(100); // Heap에 인스턴스 생성
// 다음과 같이 == 연산자를 사용하여 비교하면 안된다.
System.out.println(obj3 == obj4);
// String 클래스의 경우처럼
// Integer 클래스가 오버라이딩한 equals()를 호출해야 한다.
System.out.println(obj3.equals(obj4));
// 결론:
// => wrapper 객체를 생성할 때는 new 를 사용하지 말고,
// valueOf() 나 auto-boxing 기능을 이용하라.
// => 값을 비교할 때는 반드시 equals()를 사용하라!
//
// '==' 연산자를 사용하면 안되는가?
// => auto-boxing으로 객체를 만들 경우
// -128 ~ 127 범위 내의 숫자인 경우는 캐시에 보관하기 때문에
// 같은 값은 같은 객체이지만,
// 이 범위를 벗어나면 값이 같더라도 객체가 다르다.
// 따라서 일관성을 위해 값을 비교할 때 equals()를 사용하라!
//
// 참고:
// => 모든 wrapper 클래스는 String 클래스처럼
// 상속 받은 Object의 equals()를 오버라이딩 하였다.
// => 즉 인스턴스를 비교하는 것이 아니라 값이 같은지를 비교한다.
}
}
true
false
true
생성자 활용)
// java.util.Calendar 클래스 - 생성자 활용
package com.eomcs.basic.ex02;
import java.util.Calendar;
public class Exam0410 {
public static void main(String[] args) {
Calendar c1;
// 생성자가 있다하더라도 접근 권한이 없으면 호출할 수 없다.
// c1 = new Calendar(); // 컴파일 오류!
// Calendar는 인스턴스 생성을 도와주는 별도의 클래스 메서드(스태틱 메서드)를 제공한다.
c1 = Calendar.getInstance();
System.out.println(c1.get(1)); // year
System.out.println(c1.get(2) + 1); // month
System.out.println(c1.get(5)); // date
System.out.println(c1.get(10)); // hour
System.out.println(c1.get(9)); // am/pm
System.out.println(c1.get(12)); // minute
System.out.println(c1.get(13)); //seconds
System.out.println(c1.get(Calendar.YEAR));
System.out.println(c1.get(Calendar.MONTH) + 1);
System.out.println(c1.get(Calendar.DATE));
System.out.println(c1.get(Calendar.HOUR));
System.out.println(c1.get(Calendar.AM_PM));
System.out.println(c1.get(Calendar.MINUTE));
System.out.println(c1.get(Calendar.SECOND));
}
}
// 객체 생성 디자인 패턴 중 일부 소개
//
// 1) 팩토리 메서드(factory method)
// - GoF(Gang of Four)의 23가지 디자인 패턴(design pattern) 중 하나이다.
// - 인스턴스를 생성해주는 메서드이다.
// - 인스턴스 생성 과정이 복잡할 경우에 인스턴스를 생성해주는 메서드를 미리 정의해 둔다.
// - 그래서 인스턴스가 필요할 때 마다 메서드를 호출하여 인스턴스를 리턴 받는다.
//
// 2) 싱글톤(singleton)
// - GoF(Gang of Four)의 23가지 디자인 패턴(design pattern) 중 하나이다.
// - 인스턴스를 한 개만 생성하도록 제한할 때 사용한다.
// - 생성자를 private으로 처리하여 직접 인스턴스를 생성하지 못하도록 만든다.
// - 메서드를 통해 인스턴스를 생성하도록 유도한다.
2023
6
24
3
1
21
50
2023
6
24
3
1
21
50
'[Naver Cloud Camp 7] 교육 정리' 카테고리의 다른 글
네이버 클라우드 캠프 44일차 230627 (0) | 2023.06.27 |
---|---|
네이버 클라우드 캠프 43일차 230626 (1) | 2023.06.27 |
네이버 클라우드 캠프 41일차 230622 (0) | 2023.06.22 |
네이버 클라우드 캠프 40일차 230621 (0) | 2023.06.21 |
네이버 클라우드 캠프 39일차 230620 (0) | 2023.06.20 |