String과 StringBuffer & StringBuilder 클래스의 가장 큰 차이점은 String은 불변성(immutable)
을 갖는다는 점입니다.
1. String str = "hello"; // String str = new String("hello");
2. str = str + " world";
(GC: Garbage Collection, Memory: 정확하게는 Heap영역의 String pool)
위 사진과 같이 String은 불변성을 가지기 때문에 변하지 않는 문자열을 자주 읽어들이는 경우 String클래스를 사용해주면 좋은 성능을 기대할 수 있다. 그러나 문자열의 추가, 수정, 삭제 등의 연산
(이하, 문자열 연산)이 빈번하게 발생하는 로직에서 String클래스를 사용하면 Heap 메모리영역에 많은 임시 Gargabe가 생성되어 Heap 메모리가 부족
하게 되어 애플리케이션 성능에 큰 영향을 끼치게 된다.
이를 해결하기 위해 Java에서는 가변성(mutable)
을 가지는 StringBuffer & StringBuilder
클래스를 도입했습니다. String과는 반대로 두 클래스는 가변성을 가지기 때문에 append(), delete() 등의 API를 이용하여 동일 객체내에서 문자열을 변경하는 것이 가능합니다. 따라서 문자열 연산
가 빈번하게 발생할 경우라면 String 클래스가 아니라 StringBuffer 또는 StringBuilder 클래스를 사용하는 것이 좋다.
StringBuffer sb = new StringBuffer("hello");
sb.append(" world");
동일한 API를 가지고 있는 StringBuffer와 StringBuilder의 차이점은 무엇일까?
가장 큰 차이점은 동기화의 유무
로써 StringBuffer는 동기화를 지원하여 멀티쓰레드 환경에서 안정하다는 점(thread-safe
)이다. 참고로 String
도 불변성을 가지기 때문에 마찬가지로 thread-safe
하다는 특징을 가진다.
비교조건 | String | StringBuffer | StringBuilder |
---|---|---|---|
저장공간 | String pool(in Heap영역) | Heap 영역 | Heap 영역 |
변경가능 가능한가 | X | O | O |
thread-safe한가 (동기화 지원하는가) | O | O | X |
처리 속도 | 빠름 | 느림 | 빠름 |
~ wrpper 클래스 쓰자 ~
https://coding-factory.tistory.com/547
Integer n1 = Integer.valueOf(5);
Integer n2 = Integer.valueOf("1234");
int num = n1; // 언박싱(Unboxing)
Ingeger n3 = 5; // 박싱(Boxing)
Integer.toBinaryString(20);
Integer.toOctalString(20);
Integer.toHexString(20);
Long.MAX_VALUE; 9천경
Long.MIN_VALUE; -9천경
// 9천경이 넘어갈 경우 대안
BigInteger b1 = new BigInteger("10000000000000000000000");
[0930_13시 녹화듣기]
자바 15버전 이상 되면서 아래의 내용이 새롭게 등장했습니다.
print(객체) -> default로 객체의 toString()함수를 호출한다.
class Apple{
@Override
public Strng toString() {return "im a apple";}
}
class Orange{
@Override
public String toString() { return "im a orange"; }
}
// Apple, Orange 다 담을 수 있는 Box
class Box{
private Object ob;
public void set(Object o) {this.ob = o;}
public Object get() {return this.ob;}
}
public class GenericPractice1{
Box aBox = new Box();
Box oBox = new Box();
aBox.set(new Apple());
oBox.set(new Box());
Apple ap = (Apple) aBox.get();
Orange og = (Orange) oBox.get();
Sysout(ap); // im a apple
sysout(og); // im a orange
}
어쩔 수 없이 형변환(type casting)
의 과정이 수반된다
그리고 이는 컴파일러의 오류발견가능성을 낮춘다.
컴파일에러가 발생하지 않고 실행중간에 에러가 발생한다. 즉, 실시간 에러가 발생한다.
프로그래머의 실수가 실행과정에서 조차 발견되지 않을 수 있다.
Object ob = "Apple"; 이 가능했기 때문에 위와 같은 에러가 발생한다.
"Apple".toString(); 되면서 의도치 않는 결과가 나와서
SW적으로 위와 같은 문제를 해결한 것이 아니라
제네릭 문법을 별도로 만들어서 우리가 지키게 만들었다.
(제네릭 문법으로 인해 위와 같은 상황이 발생하면 바로 컴파일에러를 띄움)
인스턴스 생성시 결정이 되는 자료형의 정보를 T로 대체한다.
(모든 객체의 최상위조상인 Object로 받는 것 x)
T를 타입매개변수라고 한다.
set, get()함수 안에 인자로 있는 타입 T가 매개변수화 타입
이다.
class Apple{
}
class Orange{
}
class Box<T>{
private T ob ~ 위 사진처럼 코드 쓰자
}
public class GenericPractice1{
public static void main(String[] args){
Box<Apple> aBox = new Box<Apple>();
Box<Orange> oBox = new Box<Orange>();
aBox.set(new Apple());
oBox.set(new Orange());
Apple ap = aBox.get();
Orange og = oBox.get();
System.out.println(ap); // im a apple
System.out.println(og); // im a orange
}
}
Apple ap = (Apple) aBox.get();
하지 않아도됨aBox.set("Apple");
하면 컴파일 에러남 Box<Apple> aBox = new Box<Apple>();
했기 때문임[발생하는 컴파일 에러]
Unsolved compilation problem
not applicable for the arguments
public class GenericPractice1 {
static void printArray_noGeneric(Object[] obArray) {
for (Object ob : obArray) {
if(ob instanceof String) {
System.out.print((String) ob);
} else if(ob instanceof Integer) {
System.out.print((Integer) ob);
}
}
}
static <T> void printArray_Generic(T[] tArray) {
for (T t : tArray) {
System.out.print(t);
}
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3};
String[] strArray = {"Hellow", "World"};
printArray_noGeneric(intArray);
printArray_noGeneric(strArray);
System.out.println();
printArray_Generic(intArray);
printArray_Generic(strArray);
}
}
class Box<T extends Number>{
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return this.t;
}
}
// Integer와 Dobule Wrapper클래스는 Number클래스를 상속받았기 때문에 사용가능
// String, Apple 등은 사용 불가능!
Box선언할 때 할당했던 객체의 참조타입인 T 클래스안에 있는 intValue()함수를 사용하고 싶은대
만약에 T클래스에 intValue()함수가 없다면 에러가 발생할 것이다.
따라서 T클래스 참조변수 ob에서 intValeu()함수를 사용하기 위해선 Number 클래스를 이미 상속한 클래스여야 하기 때문에 위와 같이 타입인자를 제한할 수 있다.
클래스 전부가 아닌 메서드 하나에 대해 제네릭으로 정의할 경우 <T>
를 꼭 써줘야함
class Box<T>{ }
class BoxFactory{
// <T>타입을 사용할 것이고 return타입은 Box<T>이고 ㅌ타입 매개변수로 T타입을 받겠다.
public static <T> Box<T> makeBox(T o){
Box<T> box = new Box<T>();
box.set(o);
return box; //
}
}
제네릭 메서드의 T는 메서드 호출 시점에 결정한다.
Box<String> sBox = BoxFactory.<String>makeBox("Sweet"); // T가 String타입이 된다.
Box<Double> dBox = BoxFactory.<Double>makeBox(7.59); // 7.59에 대해 오토 박싱(Boxing) 진행됨
아래와 같이 타입 인자 생략할 수 있다.
Box<String> sBox = BoxFactory.makeBox("Sweet"); // "Sweet"이 String이기 때문에 생략해도 컴파일러가 알게됨
Box<Double> dBox = BoxFactory.makeBox(7.59); // 7.59에 대해 오토 박싱
날짜 정보를 얻어낼 수 있다.
Calendar now = new Calendar.getInstance(); // Calendar 객체 생성
int hour = now.get(Calendar.HOUR_OF_DAY);
int minute = now.get(Calendar.MINITE);