[java] String

toString메서드

- 인스턴스에 대한 정보를 문자열로 제공할 목적으로 정의한 것이다.

----------------------------------------------------------------

public String toString() {

        return getClass().getName() + "@" + Integer.toHexString(hashCode());

}

----------------------------------------------------------------

getClass()메서드 영역에 있는 클래스 정보들을 가지고 있는 객체를 만들어 낸다.(자바는 객체지향이므로 객체로 만들어낼수만 있으면 접근할 수 있다. ex.file)
-----------------------------------
public final native Class<?> getClass();
-----------------------------------
getName() : 클래스의 이름을 반환한다.

----------------------------------------------------------------

public String getName() {

        String name = this.name;

        if (name == null)

            this.name = name = getName0();

        return name;

}

----------------------------------------------------------------

 hashCode() : 주소값을 입력받아 int형의 해시코드를 반환
 toHexString() : 10진수 값을 16진수로 바꿔서 String으로 반환
 
+Why? 우리가 println()함수에 객체를 넣었을 경우 왜 클래스명@주소값이 나오는걸까?

----------------------------------------------------------------

public void println(Object x) {

        String s = String.valueOf(x);

        synchronized (this) {

            print(s);

            newLine();

        }

    }

 

public static String valueOf(Object obj) {

        return (obj == null) ? "null" : obj.toString();

    }

----------------------------------------------------------------

- 오버라이딩된 println(Object)에서 valueOf 호출. 객체의 toString()함수를 호출해서 가져온 String값을 print()함수로 넘겨준다.
 
 
clone메서드
- 객체 자신을 복제해서 새로운 객체를 생성하는 메서드이다.(Deep Copy)
- 단순히 멤버변수 값만을 복사하기 때문에 배열이나 인스턴스가 멤버로 정의되어 있는 클래스의 인스턴스는 완전한 복제가 이루어지지않는다.
ex) 배열의 경우, 복제된 인스턴스도 같은 배열의 주소를 갖기 때문에 복제된 인스턴스의 작업이 원래의 인스턴스에 영향을 미치게 된다. 이런 경우 clone메서드를 오버라이딩해서 새로운 배열을 생성하고 배열의 내용을 복사하도록 해야 한다.

----------------------------------------------------------------

protected native Object clone() throws CloneNotSupportedException;

 

class arrClass implements Cloneable{

int[] arr = {1,2,3};

 

public Object clone(){

Object obj = null;

try {

obj = super.clone();

} catch (CloneNotSupportedException e) {

// TODO: handle exception

}

return obj;

}

}

----------------------------------------------------------------

- Cloneable인터페이스를 구현한 클래스에서만 clone()을 호출할 수 있다. 이 인터페이스를 구현하지 않고 clone()을 호출하면 예외가 발생한다.
- clone메서드에는 CloneNotSupportedException이 선언되어 있으므로 이 메서드를 호출할 때는 try-catch문을 사용해야 한다.
- clone메서드의 접근 제어자가 protected이므로 protected, public로 바꾸고 조상인 Object클래스의 clone메서드를 통해 인스턴스를 복제하도록 오버라이딩 한다.
 
☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★
 

----------------------------------------------------------------

ex)
int [] arr=  {1,2,3,4,5};  // 이때 1,2,3,4,5는 배열 arr의 멤버변수이다.
int [] arrClone = arr.clone();
//멤버변수값이 같은 객체가 생성되어 그 주소값을 arrClone이 가진다.
 
arrClone[0] = 6;
 
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arrClone));
 
실행결과
[1, 2, 3, 4, 5]
[6, 2, 3, 4, 5]

----------------------------------------------------------------

 
- 복제할 클래스는 Cloneable를 구현(implements)해줘야 한다.

API 문서 참고-----------------------------------------------------------java.lang.Cloneable

A class implements the Cloneable interface to indicate to the java.lang.Object.clone() method that it is legal for that method to make a field-for-field copy of instances of that class.

Invoking Object's clone method on an instance that does not implement the Cloneable interface results in the exception CloneNotSupportedException being thrown.

By convention, classes that implement this interface should override

Object.clone

(which is protected) with a public method. See java.lang.Object.clone() for details on overriding this method.

Note that this interface does not contain the

clone

method. Therefore, it is not possible to clone an object merely by virtue of the fact that it implements this interface. Even if the clone method is invoked reflectively, there is no guarantee that it will succeed.

Since:
JDK1.0
Author:
unascribed
See Also:
java.lang.CloneNotSupportedException
java.lang.Object.clone()

----------------------------------------------------------------

Cloneable은 아무것도 가지고 있지 않다. 다만 복제할수 있다라는 속성만 추가할 뿐이다. 객체의 다형성중 분류를 위한 타입을 가지는 것이다.
 
getClass()
- 자신이 속한 클래스의 Class 객체를 반환하는 메서드.
- Class객체를 얻는 여러가지 방법
1.
Card c = new Card();
Class cObj = c.getClass();
2.
Class cObj = Card.class;
String className = cObj.getName();
3.
Class cObj = Class.forName("Card(클래스명)");
└ 얻어내서 어떻게 사용하나?
Card c = new Card();
Card c = (Card)cObj.newInstance();  //newInstance()는 오브젝트 타입의 객체를 리턴
 
String className = Card.class.getName();

 

String 클래스

- 실제로는 char형의 배열에 값들이 저장된다.

----------------------------------------------------------------

public final class String

    implements java.io.Serializable, Comparable<String>, CharSequence {

    /** The value is used for character storage. */

    private final char value[];

(생략)

.

.

----------------------------------------------------------------

- String 인스턴스의 내용은 바꿀 수 없다.(immutable)

String a = "a"; -> 0x100 ["a"] | a [0x100]

String b = "b"; -> 0x200 ["b"] | b [0x200]

String a = a+b; -> 0x300 ["ab"] | a [0x300]

// 붙이기 연산자를 이용하면 또다른 객체가 생성된다.

 

- 리터럴과 new를 이용한 생성의 차이점 : 생성되는 메모리 공간이 다르다. new를 사용하면 객체가 생성되므로 heap영역에 생성되며 리터럴을 사용시 리터럴은 상수영역에 생성된다. 따라서 리터럴로 생성하면 같은 값을 지니며, new를 사용하여 생성하면 같은 값을 가지는 다른 객체가 생성된다.(아래 예제 참고)

(+ 추가 팁

리터럴의 특징 : 리터럴은 상수영역에 생성되며 같은 값일 경우 한번만 생성된다.

String.equals() : String클래스는 equals()를 오버라이딩하여 제공한다.)

----------------------------------------------------------------

예제

String str1 = "abc";

String str2 = "abc";

 

System.out.println(str1 == str2);

System.out.println(str1.equals(str2));

System.out.println(System.identityHashCode("abc"));

System.out.println(System.identityHashCode(str1));

System.out.println(System.identityHashCode(str2));

System.out.println();

 

String str3 = new String("abc");

String str4 = new String("abc");

System.out.println(str3 == str4);

 

System.out.println(str3.equals(str4));

 

실행결과

true  // 상수영역에 있는 동일한 리터럴을 참조하고 있으므로 주소값이 같다.

true  // 리터럴의 값도 같다.

1426238427  // 리터럴의 주소값

1426238427  // 리터럴의 주소를 참조하는 str1의 값

1426238427  // 위와 동일. 따라서 모두 같은 값을 출력한다.

 

false  // 서로 다른 객체이므로 참조하고 있는 주소값이 다르다.

true  // 저장하고 있는 리터럴은 같다.

----------------------------------------------------------------

 
String.intern() - 객체가 가지고 있는 값을 상수로 만들어 상수영역에 저장한다. 이미 존재하는 상수이면 존재하는 상수의 주소값을 리턴한다.

 

------------------------------------------------------------

String [] words = {new String("aaa"), new String("bbb"), new String("ccc")};

 

for(int i=0; i<words.length;i++){

if(words[i].equals("ccc"))

System.out.println("words equals().");

if(words[i] == "ccc")

System.out.println("words ==.");

}

 

for(int i=0;i<words.length;i++)

words[i] = words[i].intern();

System.out.println("<< after words intern(); >>");

 

for(int i=0; i<words.length;i++){

if(words[i].equals("ccc"))

System.out.println("words equals().");

if(words[i] == "ccc")

System.out.println("words ==.");

}

------------------------------------------------------------

word[i] == "abc"

겉으로만 볼 경우 word의 i번 방에 있는 리터럴과 abc를 비교하여 true가 나올것 같지만 결과는 false다. abc는 상수영역에 저장되어있는 주소를 리턴하고 word[i]는 힙 영역에 생성된 객체의 방 주소값을 리턴한다. 따라서 서로 다른 주소값을 가지고 있으므로 결과는 false이다.

 

words[i] = words[i].intern();

words[i]번 방에 있는 값을 상수화시킨다. 이때 상수화 시키는 값이 이미 상수영역에 있는 값이면 해당 주소값을 리턴한다. 그러므로 이후에 결과값으로는 이퀄문과 ==문 모두 true가 된다.

=> 주소값이 같다라는 것만으로도 같은 객체임을 알 수 있다.

 

빈문자열("", empty string)

- 내용이 없는 문자열, 크기가 0인 char형 배열을 저장하는 문자열.

- 빈문자열로 String형을 초기화 해주면 NullPointerException을 예방할 수 있다. 따라서 null로 초기화 하기보다는 빈문자열로 초기화 하기를 권장.

- char c = '';는 불가능. char형은 '\u0000'(유니코드의 첫 번째 문자로써 아무런 문자도 지정되지 않은 빈 문자)보다 공백으로 초기화 하기를 권장.

 

String 변환

valueOf : 기본타입을 String객체로 반환한다.

wrapClass.parse* : 문자열을 *형으로 변환하여 반환한다.(ex. Integer.parseInt() )

 

String 메소드

indexOf : 해당 값이 들어있는 방의 인덱스 번호를 반환한다. 만약 존재하지 않으면 음수를 반환한다.

substring : 주어진 시작위치(begin)부터 끝 위치(end)범위에 포함된 문자열을 얻는다. 이 때, 시작위치의 문자는 범위에 포함되지만, 끝 위치의 문자는 포함되지 않는다. (begin<=x<end)

└ String substring(int begin), String substring(int begin, int end)

더보기

------------------------------------------------------------

public class _18_StringCount {

private int count;

private String source = "";

 

public _18_StringCount(String source){

this.source = source;

}

 

public int stringCount(String s){

return stringCount(s,0);

}

 

public int stringCount(String s, int pos){

int index = 0;  // 단순 지역변수 초기화

if(s == null || s.length() == 0)

return 0;

if((index = source.indexOf(s,pos)) != -1){  // 해당 값 들어있는 방 번호를 인덱스에 넣어주고

count++;

stringCount(s, index+s.length());  // 이미 검사한 부분은 안해도 되니까 index+length

}

return count;

}

public static void main(String[] args) {

// TODO Auto-generated method stub

String str = "aabbccAABBCCaa";

_18_StringCount sc = new _18_StringCount(str);

System.out.println("found "+sc.stringCount("aa")+ "aa.");  // 붙이기 연산자(+)를 이용하면 새로운 String객체가 생성된다. 즉 편집이 불가하다. substring의 경우도 마찬가지이다. <=> 변경이 가능하게 하고 싶으면 StringBuffer 사용하라

}

 

 

}

------------------------------------------------------------

 

StringBuffer (Buffer란? 임시저장공간)

- String와 StringBuffer의 공통점으로는 문자형 배열을 내부적으로 가지고 있다. 그러나, String클래스와 달리 내용을 변경할 수 있다.(mutable) (ex) append()를 사용하여 수정할 수 있다.)

- 인스턴스를 생성할 때 버퍼(배열)의 크기를 충분히 지정해주는 것이 좋다. 버퍼가 작으면 성능저하가 일어나며 작업 중에 더 큰 배열의 생성이 필요하다.

- String클래스와 달리 equals()를 오버라이딩하지 않았다. 따라서 Object클래스가 제공하는 equals()를 사용한다.

 

StringBuffer메소드

append() : 매개변수로 입력된 값을 문자열로 변환하여 StringBuffer인스턴스가 저장하고 있는 문자열의 뒤에 덧붙인다.

toString() : StringBuffer인스턴스의 문자열을 String 객체로 반환한다.

더보기

------------------------------------------

public synchronized String toString() {

        return new String(value, 0, count);

    }

------------------------------------------

 

Wraper Class

- 기본 데이터 타입과  wraper클래스간에 형변환이 가능하다.

- Integer + 10 => autoboxing(Integer를 벗겨내고 계산이 이루어진다.)