String클래스는 불변이라는 특성에 의해 지정된 문자열을 변경할 수 없지만 StringBuffer클래스의 인스턴스는 변경이 가능하다. 인스턴스 내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있으며, StringBuffer 인스턴스를 생성할 버퍼의 크기를 지정해줄 수 있다.
StringBuffer는 인스턴스를 생성할때, 버퍼의 크기를 지정해주지 않으면 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성한다. 또한 문자열을 인자로 넣어 생성하면 해당 문자열의 길이 +16의 길이에 해당하는 문자열 버퍼를 생성한다.
문자열의 길이가 버퍼의 크기보다 커질경우 버퍼의 크기는 변경될수 없으므로 새로운 길이의 버퍼를 생성한후 기존의 버퍼에 저장된 데이터를 복사한다.
StringBuffer는 String클래스와 달리 내용을 변경할 수 있다.
ex)
StringBuffer a = new StringBuffer("abc");
a.append("def");
//a.equals("abcdef") == true
위와같이 String 클래스는 새로운 문자열을 만들어 해당 문자열을 가리키는 반면 StringBuffer 기존의 문자열("abc")에 "def"를 더할 수 있다.
StringBuffer클래스의 비교
StringBuffer클래스는 equals메소드가 오버라이딩 되어있지않아 equals메소드와 == 이 같다. 두개모두 값이아닌 주소를 비교한다. StringBuffer인스턴스의 데이터를 비교하기 위해서는 toString을 호출하여 String클래스에 저장하고 저장된 데이터를 equals 메소드를 통해 비교해야한다.
StringBuffer는 equals메소드도 비교가 불가능하기 때문에 꼭 toString()메소드를 통해 String 인스턴스를 만들어 값을 넣어주고 해당 String 인스턴스간에 equals메소드로 비교를 해준다.
StringBuffer클래스의 메소드
StringBuffer(int length) : length에 해당하는 길이의 버퍼를 가진 StringBuffer를 생성한다.
StringBuffer(String str) : 문자열(str)을 갖는 StringBuffer인스턴스를 생성한다. StringBuffer의 길이는 str.length + 16 이다.
StringBuffer append(boolean b)
StringBuffer append(char c)
StringBuffer append(char[] str)
StringBuffer append(double d)
StringBuffer append(float f)
StringBuffer append(int i)
StringBuffer append(long l)
StringBuffer append(Object obj)
StringBuffer append(String str)
입력된 매개변수의 값을 문자열로 변환하여 StringBuffer인스턴스의 문자열 뒤에 붙인다.
int capacity() : StringBuffer 인스턴스의 버퍼크기를 알려준다.
int length() : StringBuffer 인스턴스에 저장되어있는 문자열의 길이를 반환해준다.
char charAt(int index) : 지정된 위치(index)에 있는 문자를 반환한다.
StringBuffer delete(int start, int end) : 시작위치(start)부터 끝위치(end)까지의 문자열을 제거한다. 끝위치의 문자는 제외한다.
StringBuffer deleteCharAt(int index) : 지정된 위치(index)의 문자를 제거한다.
StringBuffer insert(int pos, boolean b)
StringBuffer insert(int pos, char c)
StringBuffer insert(int pos, char[] str)
StringBuffer insert(int pos, double d)
StringBuffer insert(int pos, float f)
StringBuffer insert(int pos, int i)
StringBuffer insert(int pos, long l)
StringBuffer insert(int pos, Object obj)
StringBuffer insert(int pos, String str)
두번째 매개변수로 받은 값을 문자열로 변환하여 지정된 위치(pos)에 추가한다. pos는 0부터 시작
StringBuffer replace(int start, int end, String str) : start~end의 문자열을 주어진 문자열(str)로 바꾼다. end는 포함되지 않는다.
StringBuffer reverse() : StringBuffer 인스턴스에 저장되어 있는 문자열의 순서를 거꾸로 나열한다.
void setCharAt(int index, char ch) : 지정된 위치의 문자를 주어진 문자(Ch)로 바꾼다.
void setLength(int newLength) : 문자열의 길이를 newLength길이로 변경한다. 길이를 늘리는 경우에 나머지 빈 공간을 '\u0000'로 채운다.
int indexOf(int ch) : 문자(ch)가 문자열의 어느 위치에 있는지 확인하여 위치(index)를 반환해준다. 문자열에 존재하지 않는다면 -1을 반환한다.
int indexOf(int ch, int pos) : pos위치부터 문자(ch)가 위치를 알려준다.
int indexOf(String str) : 문자열(str)이 문자열의 어느위치에 있는지 확인하여 시작하는 위치(index)를 반환해준다. 문자열(str)이 문자열에 존재하지 않는다면 -1을 반환한다.
int lastIndexOf(int ch), int lastIndexOf(String str) : 위의 함수들과 같지만 문자열의 가장 오른쪽(맨끝) 부터 왼쪽으로 이동하면서 확인한다.
int length() : 문자열의 길이를 반환한다.
String replace(char old, char new) : 문자열중 문자(old)를 새로운 문자(new)로 바꾼 문자열을 반환한다.
String replace(CharSequence old, CharSequence new) : 문자열중 문자열(old)을 새로운 문자열(new)로 모두 바꾼 문자열을 반환한다.
String replaceAll(String regex, String replacement) : 문자열중 지정된 문자열(regex)과 일치하는 모든 문자열을 문자열(replacement)로 변경한다.
String replaceFirst(String regex, String replacement) : 문자열중 지정된 문자열(regex)과 일치하는 것 중, 첫번째로 일치하는 문자열만 문자열(replacement)로 변경한다.
String[] split(String regex) : 문자열을 지정된 분리자(regex)로 나누어 문자열 배열에 담아 반환한다.
String[] split(String regex, int limit) : 문자열을 지정된 분리자(regex)로 나누어 문자열배열에 담아 반환한다. 단, 문자열 전체를 지정된 수(limit)로 자른다.
String substring(int begin), String substring(int begin, int end) : 시작위치(begin)부터 끝 위치(end) 범위에 포함된 문자열을 얻는다. 이때, 시작위치의 문자는 범위에 포함되지만, 끝 위치의 문자는 포함되지 않는다.
String toLowerCase() : String인스턴스에 저장되어 있는 모든 문자열을 소문자로 변환하여 반환한다.
String toUpperCase() : String인스턴스에 저장되어 있는 모든 문자열을 대문자로 변환하여 반환한다.
String toString() : String인스턴스에 저장되어 있는 문자열을 반환한다.
String trim() : 문자열의 왼쪽 끝과 오른쪽 끝에 있는 공백을 지운 결과를 반환한다. 문자열 중간에 있는 공백은 지워지지않는다.
join()메소드와 StringJoiner
String의 join()메소드를 통해 여러 문자열 사이에 구분자를 넣어서 결합할 수 있다.
StringJoiner클래스를 사용해서도 문자열 결합이 가능하다.
join()메소드와 StringJoiner클래스를 사용했을때이다.
join()메소드를 통해 해당 문자열배열 사이에 "-"를 넣어 결합한형태이고
StringJoiner클래스를 통해 구분자 "/", 시작문자 "[", 끝문자"]"를 결합한 상태이다.
StringJoiner에 add메소드를 통해 각 문자열들을 결합해준다.
기본형 값을 String으로 변환
일반적으로 숫자를 문자로 변환할 경우에는 숫자에 빈 문자열""을 더해주는 방식으로 숫자를 문자열로 변환한다.
숫자를 문자열로 변환하는 방법으로 String클래스의 valueOf()메소드가 있다. 성능은 valueOf()메소드가 더 좋기때문에 성능향상이 필요한경우가 아니라면 빈문자열을 더하는 방법이 더 편하다.
반대로 문자열을 기본형 값으로 변환하는 것도 valueOf()로 가능하다.
ex)
int i = Integer.valueOf("100");
int i2 = Integer.parseInt("100");
문자열의 데이터에 해당하는 데이터형 클래스의 메소드의 parse를 사용해서도 해당하는 데이터로 변환가능하다.
ex)
Byte.parseByte(String s);
Short.parseShort(String s);
Integer.parseInt(String s);
Long.parseLong(String s);
Float.parseFloat(String s);
Double.parseDouble(String s);
위의 클래스의 각각 valueOf(string str)을 사용해도 해당 문자열을 위의 클래스로 변환이 가능하다.
parseInt나 parseDouble같은 메소드는 문자열에 공백이 포함되어있는 경우 예외가 발생할 수 있으므로 주의해야한다.
그래서 trim()메소드를 통해 공백을 제거해주기도한다.
부호를 의미하는 '+'나 float형 값을 뜻하는 'f'와 같은 자료형 접미사는 포함 가능하다.
==의 경우 두 객체의 참조변수 값의 같고 다름을 판단하기 때문에 해당 기호는 주소값의 비교이다.
Object의 equasl()메소드도 참조 변수의 값으로 판단을 하는데 이것을 객체의 값 비교로 사용하기 위해서는 적절한 오버라이딩이 필요하다.
객체에 저장된 값을 비교하기 위해서는 equals 메서드를 오버라이딩 하여 사용하는데 해당 메서드를 값비교를 위해 오버라이딩한 예제이다.
String 클래스에 저장된 문자열 비교시에도 ==이 아닌 equals 메소드를 사용한다.
생성된 객체들은 모두 주소가 다르기 때문에 ==에서 다른 값이라고 인식되고, 오버라이딩 된 equals를 통해 객체의 주소 값이 아닌 객체의 값을 비교했을 때 같은 값을 가지고 있기 때문에 같은사람이라는 출력이나온다.
HashCode() 메서드
Object클래스의 hashCode메서드는 객체의 주소값으로 해쉬값을 만들기 때문에 객체마다 다른 해쉬값을 가진다.
(System.identityHashCode()와 Object클래스의 hashCode는 같은 방식으로 동작한다.)
인스턴스 값에대한 hashcode를 생성하여 같은 값은 같은 hash값을 갖기 위해서는 hashcode 메서드를 오버라이딩 하여 사용 해야한다.
String 클래스의 경우 String객체가 가진 문자열 값을 기반으로 HashCode를 생성하도록 오버라이딩 되어있으므로 위의 예제는 같은 HashCode를 출력한다. 그러나 Object의 hashcode원리와 같이 주소값을 기반으로 hashCode를 생성할 경우 두개의 객체는 다른 값을 출력한다.
또한 equals()를 오버라이딩 하는 상황이라면 hashCodoe()메서드도 적절하게 오버라이딩 해주어야 한다. 같은 객체라면 HashCode()메서드의 결과값도 같아야 하기 때문이다.
toString() 메서드
Object 클래스에 정의된 toString()
Object클래스에 정의된 toString()메서드의 경우 클래스이름@주소기반16진수hashcode문자열이 반환된다.
하지만 위의 정보들은 사용시 유용하지 않기 때문에 toString()메서드는 일반적으로 인스턴스 클래스에 대한 정보나 인스턴스 변수들의 값을 문자열로 반환하도록 오버라이딩하여 사용된다.
클래스에서 toString()메서드를 오버라이딩 한경우
위와같이 인스턴스 객체의 인스턴스 변수들을 출력하게 오버라이딩 후 출력하였다. Object클래스의 toString()메서드가 public 메서드이기 때문에 오버라이딩의 경우도 public으로 해주어야 한다.
clone() 메서드
clone() 메서드는 자신을 복제하여 새로운 객체를 생성하는 메서드이다.
Object클래스에 정의된 clone()은 단순히 인스턴스변수의 값만 복사하기 때문에 참조타입의 인스턴스 변수가 있는 클래스는 완전한 인스턴스 복제가 이루어지지 않는다.
clone()을 사용하려면, 먼저 clone메서드를 사용할 클래스가 Cloneable인터페이스를 구현해주어야한다.
Cloneable인터페이스를 구현한 클래스의 인스턴스만 clone()메서드를 사용할 수 있는 이유는 인스턴스 데이터를 보호하기 위해서이다. 해당 클래스의 인스턴스만 복제를 허용한다.
또한 clone()메서드를 호출할때는 반드시 예외처리를 해주어야한다.
clone()메서드는 객체에 저장된 값을 그대로 복제하지만, 객체가 참조하고있는 객체까지 복제하지 못한다. 객체가 참조하고있는 주소만을 복제할 뿐이다. 이러한 복제는 해당 객체의 주소를 복제하고 객체자체를 복제하는 것이 아니기 때문에 완전한 복제라고 보기어렵다. 객체의 주소가아닌 참조객체까지 복제하는 '깊은 복제(deep copy)'를 하기 위해서는 clone()메서드를 이용해서 적절하게 구현 해주어야한다.
shallow copy는 객체에 저장된 값을 그대로 복제하는 얕은복사이다. 참조변수 멤버가 있을경우 참조변수의 참조값 즉 주소값만 복제가된다.
deepcopy메서드의 경우 해당 참조변수 멤버의 객체값을 복사한다.
얕은 복사를 진행한 c2의 경우 원본인 c1의 값이 바뀔경우 c2의 값도 변경된다.
그러나 깊은 복사를 진행한 c3의 경우 c1이 변경되더라도 원본의 값을 유지한다.
깊은 복사를 통해 원본이 참조하고있는 Point객체까지 복제했기 때문에 위와같은 결과가 나타난다.
인스턴스 변수에 참조형 변수가 없는경우 clone()메서드만으로 충분하다.
그러나 위처럼 깊은복사를 clone메소드를 통해 구현하고싶다면 참조변수의 객체를 복제가 가능하도록 깊은복사 메서드를 구현 해주어야한다.
getClass() 메서드
getClass()메서드는 자신이 속한 클래스의 Class 객체를 반환하는 메서드이다. Class 객체는 이름이 'Class'인 클래스 객체이다.
Class객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재한다. 클래스 파일이 '클래스 로더(ClassLoader)'에 의해 메모리에 올라갈 때, 자동으로 생성된다. 클래스 로더는 실행시에 클래스를 동적으로 메모리에 로드하는 역할을 한다.
getClass()메서드는 파일형태로 저장되어 있는 클래스를 읽어서 Class클래스에 정의된 형식으로 변환하는 것이다.
Class 객체에 대한 참조를 얻는 방법으로 클래스명.class.newInstance()를 사용했다.
해당 Class객체를 통해 해당 객체의 클래스 이름을 얻을 수 있다.
Class객체가 실무에서 자주 쓰일지는 잘모르겠다. 뭔가 난해하고 유용한가에 대한 의문이 생긴다.