1. 문자열 객체
1.1 Heap과 String pool의 관계
- new String()
- new String()으로 생성한 객체는 Heap영역에 보관된다.
String s1 = new String("Hello");
String s2 = new String("Hello");
-> s1과 s2의 인스턴스는 다르다
- String pool
- ""으로 할당된 문자열은 String pool에 생성된다.
- 다른 변수에 String pool에 있는 문자열을 할당하면 동일한 인스턴스를 가진다.
String x1 = "Hello"; // 새 String 인스턴스의 주소를 리턴한다.
String x2 = "Hello"; // 기존의 String 인스턴스 주소를 리턴한다.
-> 두객체의 인스턴스는 같다.
- 차이점
- 두 방식의 차이점을 Heap에 인스턴스를 생성하냐, String Pool에 인스턴스를 생성하냐의 차이이다.
- 근본적으로 두 방식 모두 String 객체의 인스턴스이다.
String s1 = new String("Hello"); // Heap 영역에 String 인스턴스를 만든다.
String s2 = "Hello"; // String Pool 영역에 String 인스턴스를 만든다.
System.out.println(s1 == s2); //false
System.out.println(s1 instanceof String); //true
System.out.println(s2 instanceof String); //true
- String.intern()
- intern() 메서드 : String 객체의 문자열과 동일한 문자열을 갖는 String객체를 String pool에서 찾는다.
- String pool에 있으면, 해당 주소를 리턴한다.
- String pool에 없으면, String pool에 해당 객체를 만들고 그 주소를 리턴한다.
Case 1.
String s1 = new String("Hello"); // Heap 영역에 String 인스턴스를 생성한다.
String s2 = s1.intern(); //String pool에 Hello를 생성
String s3 = "Hello"; // 해당 문자열을 가진 String 객체를 String Pool에서 찾는다.
System.out.println(s1 == s2); //false
System.out.println(s2 == s3); //true
Case 2.
String s1 = new String("Hello"); // Heap 영역에 String 인스턴스를 생성한다.
String s2 = "Hello"; //String pool에 Hello를 생성
String s3 = s1.intern(); //해당 문자열을 가진 String 객체를 String Pool에서 찾는다.
System.out.println(s1 == s2); //false
System.out.println(s2 == s3); //true
1.2 String.toString() 다형성
- 다음과 같이 코드가 주어질 때, 형변환을 String 객체로 대입할 수 있다.
Object obj = new String("Hello"); // 인스턴스 주소가 100이라 가정하자;
String x1 = (String) obj; // x1 <--- 100
System.out.println(obj == x1);
- Object 클래스에 선언된 멤버여도 실제 obj객체가 가리키는 클래스부터 찾아 올라간다.
- obj가 String으로 인스턴스 주소를 받았기 때문에 String 클래스의 toString()을 먼저 찾는다.
- String의 toString()이 오버라이딩 되었기 때문에 Hello를 리턴한다.
String x2 = obj.toString(); // x2 <---- 100
System.out.println(x2); // Hello
System.out.println(x1 == x2); // true
- 원래 타입으로 형변환을 해야 해당 타입의 클래스의 메소드를 호출 할 수 있다.
1.3 mutable 객체와 immutable 객체
- String 객체는 immutable로 한번 정해지면 데이터를 변경할 수 없다.
String s1 = new String("Hello");
// String 클래스의 메서드는 원본 인스턴스의 데이터를 변경하지 않는다.
// 다만 새로 String 객체를 만들 뿐이다.
String s2 = s1.replace('l', 'x');
System.out.println(s1 == s2); // false
System.out.printf("%s : %s\n", s1, s2); // 원본은 바뀌지 않는다.
String s3 = s1.concat(", world!");
System.out.println(s1 == s3); // false
System.out.printf("%s : %s\n", s1, s3); // 원본은 바뀌지 않는다.
- StringBuffer은 mutable 객체이다.
StringBuffer buf = new StringBuffer("Hello");
System.out.println(buf); // Hello
System.out.println(buf.hashCode()); //498931366
buf.replace(2, 4, "xxxx");// 원본을 바꾼다.
System.out.println(buf); //Hexxxxo
System.out.println(buf.hashCode()); //498931366
- StringBuilder은 mutable객체이다.
StringBuilder strBuilder = new StringBuilder("Hello");
System.out.println(strBuilder); // Hello
System.out.println(strBuilder.hashCode()); // 498931366
strBuilder.replace(2, 4, "xxxx");// 원본을 바꾼다.
System.out.println(strBuilder); // Hexxxxo
System.out.println(strBuilder.hashCode());// 498931366
1.4 StringBuffer와 StringBuilder
- 두 클래스의 차이는 동시성이다.
- Buffer은 멀티스레드 환경에서 순서를 정해서 인스턴스에 접근한다.
- Builder는 비동기화로 단일 스레드 환경에서 순차적으로 인스턴스에 접근한다.
1.5 String복사하기
- String을 복사하는 메서드는 Arrays.copyOfRange(args[], start,end)
- 이때 범위는 (start,end]의 범위를 가진다.
String[] arr = {"101", "제목", "내용", "4", "2021-2-2"};
// 배열에서 특정 범위의 항목을 복사하기
String[] arr2 = Arrays.copyOfRange(arr, 2, 4);
for (String s : arr2) {
System.out.println(s);
1.6 배열과 String
String s1 = new String();
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 s4 = new String(bytes);
System.out.printf("s4=%s\n", s4);
// 한글이 깨진다. 이유?
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);
2. Wrapper class
- primitive type은 객체가 아니므로 인스턴스 변수를 활용할수 없다.
- 이를 해결하기 위해 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);
2.1 wrapper class 활용
- wrapper 클래스를 사용하지 않으면 primitive type에 대해 각각 메서드를 만들어야한다.
long l = 100L;
double d = 3.14;
boolean bool = true;
m(l);
m(d);
m(bool);
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);
}
- wrapper 클래스를 사용하여 객체로 만들면 Object를 활용하여 객체를 받는 메소드로 만들 수 있다.
Long obj1 = Long.valueOf(l);
Double obj2 = Double.valueOf(d);
Boolean obj3 = Boolean.valueOf(bool);
m(obj1);
m(obj2);
m(obj3);
static void m(Object value) { // 모든 객체를 받을 수 있다.
System.out.printf("wrapper value=%s\n", value);
}
2.2 auto-boxing
- 컴파일 시 Wrapper 클래스가 적용된 객체에 대해서는 자동으로 객체로 변환한다.
int i1 = 100;
Integer obj1 = Integer.valueOf(i1);
Integer obj2 = 100; // ==> Integer.valueOf(100)
2.3 auto-unboxing
- 컴파일 시 오토 언박싱도 수행한다.
Integer obj = Integer.valueOf(100);
int i2 = obj.intvalue()
int i3 = obj; // ==> obj.intvalue()
3. 실습프로젝트
3.1 배열의 장단점
장점 | 단점 |
1. 고정된 크기 • 배열의 크기를 미리 정의하면, 메모리 관리가 용이 • 필요한 크기만큼의 메모리를 할당 |
1. 고정된 크기 • 선언 시점에 크기를 고정하여 컴파일 후 크기 불가 • 데이터의 양을 정확히 예측하기 어렵거나, 가변적인 경우에 부적합 |
2. 빠른 데이터 접근 • 배열의 인덱스를 사용하여 상수 시간 내에 O(1) 접근 • 특정 위치의 데이터를 빠르게 읽고 쓸 수 있음 |
2. 메모리 낭비 • 필요한 양보다 큰 배열을 선언하면 메모리가 낭비 • 반대로, 배열이 작으면 데이터가 초과 |
3. 연속적인 메모리 할당 • 배열은 메모리 상에 연속적으로 저장되므로, 캐시 효율성이 높음 |
3. 삽입 및 삭제의 비효율성 • 배열 중간에 요소를 삽입하거나 삭제할 때, 다른 요소를 이동 • 시간 복잡도가 O(n)인 작업으로, 대용량 데이터에서 비효율적 |
4. 간단한 구조 • 배열은 데이터 구조가 간단하고 사용법이 직관적 |
4. 타입제한 • 자바의 배열은 단일 타입의 데이터만 저장 • 다양한 타입의 데이터를 저장해야 할 때는 불편 |
3.2 가변배열 만들기
- grow() 메서드로 새로운 배열 만들기
private void grow() {
int oldSize = list.length;
int newSize = oldSize + (oldSize >> 1);
Object[] arr = new Object[newSize];
for (int i = 0; i < oldSize; i++) {
arr[i] = list[i];
}
list = arr;
}
public void add(Object obj) {
if (size == list.length) {
int oldSize = list.length;
int newSize = oldSize + (oldSize >> 1);
grow();
}
list[size++] = obj;
}
- Arrays메서드 활용
public void add(Object obj) {
if (size == list.length) {
int oldSize = list.length;
int newSize = oldSize + (oldSize >> 1);
list = Arrays.copyOf(list,newSize);
grow();
}
list[size++] = obj;
}
3.3 LinkedList 만들기
- LinkedList의 구조는 Node에 다음 Node의 레퍼런스를 가지게 함으로서 객체간에 연결을 하는 구조이다.
//노드의 기본구조
public class Node {
Object value;
Node next;
public Node(Object value) {
this.value = value;
}
}
- LinkedList 연결하는 기본원리는 새로 생성된 주소 값을 받는 것이다.
public void append(Object value) {
size++;
Node newNode = new Node(value);
//첫 노드의 경우 first와 last의 필드값이 null이다.
if (first == null) {
first = last = newNode;
return;
}
// 이후 노드는
last.next = newNode; //추가된 노드의 주소를 넘긴다
last = newNode; //추가된 노드의 객체의 주소를 넘긴다
}
- LinkedList의 조회는 다음 주소값을 넘어가고 인덱스 일치 여부를 판단하는 것이다.
public Object getValue(int index) {
if (index < 0 || index >= size) {
return null;
}
Node cursor = first;
int currentIndex = 0;
while (cursor != null) {
if (currentIndex == index) {
return cursor.value;
}
cursor = cursor.next;
currentIndex++;
}
return null;
}
- LinkedList의 삭제는 다음의 경우를 고려해야한다.
- index = 0 인경우 : first를 first.next로 넘겨주고 단일 Link인지 확인
- 중간에 위치한 경우 : index-1 지점 까지 커서를 이동 후 cusor.next = cusor.next.next와 연결한다.
- 끝지점에 위치한 경우 : last의 한칸 땡기기
public Object delete(int index) {
if (index < 0 || index >= size) {
return null;
}
size--;
Node deletedNode = null;
Node cursor = first;
int currentIndex = 0;
if (index == 0) {
deletedNode = first;
first = first.next;
if (first == null) {
last = null;
}
return deletedNode.value;
}
while (cursor != null) {
if (currentIndex == index - 1) {
break;
}
cursor = cursor.next;
currentIndex++;
}
deletedNode = cursor.next;
cursor.next = cursor.next.next;
if (cursor.next == null) {
last = cursor;
}
return deletedNode.value;
}
'개발자 꿈나무의 하루 > 01_Boot Camp' 카테고리의 다른 글
(네이버클라우드 부트캠프) 24_25일차 - 미니프로젝트 가계부만들기 (0) | 2024.07.01 |
---|---|
(네이버클라우드 부트캠프) 23일차 - 실습프로젝트(인터페이스) (0) | 2024.06.26 |
(네이버클라우드 부트캠프) 21일차 - Java프로그래밍 기초(Object 클래스) (1) | 2024.06.24 |
(네이버클라우드 부트캠프) 19일차 - 실습프로젝트(Static필드, High Cohesion) (0) | 2024.06.20 |
(네이버클라우드 부트캠프) 18일차 - 실습프로젝트(게시판CRUD) (0) | 2024.06.19 |