java.lang 패키지는 자바에서 가장 기본적인 동작을 수행하는 클래스들의 집합이다
따라서 자바에서는 java.lang 패키지의 클래스들은 import 문을 사용하지 않아도 클래스 이름만으로 바로 사용할 수 있도록 하고 있다
Object 클래스는 모든 자바 클래스의 최고 조상 클래스이기 때문에 Object 클래스의 메소드를 바로 사용할 수 있다 (따로 상속 받을 필요 없음)
이러한 Object 클래스는 필드를 가지지 않으며, 총 11개의 메소드만으로 구성되어 있다.
toString() 메소드
해당 인스턴스에 대한 정보를 문자열로 반환
반환되는 문자열은 클래스 이름과 함께 구분자로 '@'가 사용되며, 그 뒤로 16진수 해시 코드(instance의 주소)가 추가됨
Car car01 = new Car();
Car car02 = new Car();
System.out.println(car01.toString());
System.out.println(car02.toString());
// 결과
// Car@15db9742
// Car@6d06d69c
equals() 메소드
해당 인스턴스를 매개변수로 전달받는 참조 변수와 비교하여, 그 결과를 반환
== 이 있는데 왜 쓰는가?
equals()는 참조 변수가 가리키는 값을 비교하고 ==은 주소값을 비교한다
(즉 객체를 copy를 통해 똑같은 값을 가지고 있더라도 주소가 다르면 ==은 false를 리턴한다)
clone() 메소드
해당 인스턴스를 복제하여, 새로운 인스턴스를 생성해 반환한다
하지만 Object 클래스의 clone() 메소드는 단지 필드의 값만을 복사하므로, 필드의 값이 배열이나 인스턴스면 주소만 복사된다
따라서 이러한 경우에는 해당 클래스에서 clone() 메소드를 오버라이딩하여, 복제가 제대로 이루어지도록 재정의해야 한다
import java.util.*;
class Car implements Cloneable { //clone 메소드는 데이터보호의 이유로 Cloneable 인터페이스를 구현한 클래스의 인스턴스만이 사용가능하다
private String modelName;
private ArrayList<String> owners = new ArrayList<String>();
public String getModelName() { return this.modelName; }
public void setModelName(String modelName) { this.modelName = modelName; }
public ArrayList getOwners() { return this.owners; }
public void setOwners(String ownerName) { this.owners.add(ownerName); }
public Object clone() {
try {
Car clonedCar = (Car)super.clone();
// clonedCar.owners = (ArrayList)owners.clone();
return clonedCar;
} catch (CloneNotSupportedException ex) {
ex.printStackTrace();
return null;
}
}
}
public class Object03 {
public static void main(String[] args) {
Car car01 = new Car();
car01.setModelName("아반떼");
car01.setOwners("홍길동");
System.out.println("Car01 : " + car01.getModelName() + ", " + car01.getOwners() + "\n");
Car car02 = (Car)car01.clone();
car02.setOwners("이순신");
System.out.println("Car01 : " + car01.getModelName() + ", " + car01.getOwners());
System.out.println("Car02 : " + car02.getModelName() + ", " + car02.getOwners());
}
}
위 코드를 실행하면 아래와 같은 결과가 출력된다
Car01 : 아반떼, [홍길동]
Car01 : 아반떼, [홍길동, 이순신]
Car02 : 아반떼, [홍길동, 이순신]
위의 예시에서 owners라는 필드는 인스턴스이기 때문에 해당 주소값이 복사된다
따라서 위의 결과와 같이 car02 객체의 owner를 추가해도 car01 객체의 owner까지 추가된다
따라서 위의 주석과 같이 인스턴스 필드에 대해서는 별도로 clone 메소드를 구현해야 한다
// 주석 해제 후 출력값
Car01 : 아반떼, [홍길동]
Car01 : 아반떼, [홍길동]
Car02 : 아반떼, [홍길동, 이순신]
java.lang.String 클래스
문자열을 위한 클래스
String 인스턴스는 한 번 생성되면 그 값을 읽기만 할 수 있고, 변경할 수는 없다
이러한 객체를 자바에서는 불변 객체(immutable object)라고 한다
즉, 자바에서 덧셈(+) 연산자를 이용하여 문자열 결합을 수행하면, 기존 문자열의 내용이 변경되는 것이 아니라 내용이 합쳐진 새로운 String 인스턴스가 생성되는 것임
해당 문자열의 특정 인덱스에 해당하는 문자를 반환
- 만약 해당 문자열의 길이보다 큰 인덱스나 음수를 전달하면, IndexOutOfBoundsException 오류가 발생
String str = new String("Java");
System.out.println("원본 문자열 : " + str);
for (int i = 0; i < str.length(); i++) {
System.out.print(str.charAt(i) + " ");
}
System.out.println("\ncharAt() 메소드 호출 후 원본 문자열 : " + str);
// 실행결과
// 원본 문자열 : Java
// J a v a
// charAt() 메소드 호출 후 원본 문자열 : Java
compareTo() 메소드는 해당 문자열을 인수로 전달된 문자열과 사전 편찬 순으로 비교
- 두 문자열을 한글자씩 사전순으로 비교하여 같다면 0을 반환, 해당 문자열이 인수로 전달된 문자열보다 작으면 얼마나 작은지 음수로, 크면 얼마나 큰지 양수로 반환
기본적으로는 대소문자를 구분하여 비교하기 때문에 대소문자를 구분하지 않기를 원한다면, compareToIgnoreCase() 메소드를 사용하면 됨
String str = new String("abcd");
System.out.println("원본 문자열 : " + str);
System.out.println(str.compareTo("bcef"));
System.out.println(str.compareTo("abcd") + "\n");
System.out.println(str.compareTo("Abcd")); // 만일 a가 같고 b가 대문자였어도 32를 반환한다
System.out.println(str.compareToIgnoreCase("Abcd"));
System.out.println("compareTo() 메소드 호출 후 원본 문자열 : " + str);
실행결과:
원본 문자열 : abcd
-1
0
32
0
compareTo() 메소드 호출 후 원본 문자열 : abcd
concat() 메소드
해당 문자열의 뒤에 인수로 전달된 문자열을 추가한 새로운 문자열을 반환
만약 인수로 전달된 문자열의 길이가 0이면, 해당 문자열을 그대로 반환
그러면 +와 뭐가 다른가?
String str = new String("Java");
System.out.println("원본 문자열 : " + str);
System.out.println(str.concat("수업"));
System.out.println("concat() 메소드 호출 후 원본 문자열 : " + str);
실행결과:
원본 문자열 : Java
Java수업
concat() 메소드 호출 후 원본 문자열 : Java
indexOf() 메소드
해당 문자열에서 특정 문자나 문자열이 처음으로 등장하는 위치의 인덱스를 반환
- 만약 해당 문자열에 전달된 문자나 문자열이 포함되어 있지 않으면 -1을 반환
String str = new String("Oracle Java");
System.out.println("원본 문자열 : " + str);
System.out.println(str.indexOf('o'));
System.out.println(str.indexOf('a'));
System.out.println(str.indexOf("Java"));
System.out.println("indexOf() 메소드 호출 후 원본 문자열 : " + str);
원본 문자열 : Oracle Java
-1
2
7
indexOf() 메소드 호출 후 원본 문자열 : Oracle Java
대소문자 구분함
trim() 메소드
해당 문자열의 맨 앞과 맨 뒤에 포함된 모든 공백 문자를 제거
toLowerCase(), toUpperCase() 메소드
toLowerCase() 메소드는 해당 문자열의 모든 문자를 소문자로 변환시켜 줌
toUpperCase() 메소드는 해당 문자열의 모든 문자를 대문자로 변환시켜 줌
String str = new String("Java");
System.out.println("원본 문자열 : " + str);
System.out.println(str.toLowerCase());
System.out.println(str.toUpperCase());
System.out.println("두 메소드 호출 후 원본 문자열 : " + str);
실행결과:
원본 문자열 : Java
java
JAVA
두 메소드 호출 후 원본 문자열 : Java
이 외에도 아래와 같이 다양한 메소드들이 있음
String 클래스의 인스턴스는 생성후 변경할 수 없는 불변 객체이므로, 문자열을 변경하고 추가할 수 있도록 하기 위해 나온 클래스 (가변 클래스)
이를 위해 StringBuffer 클래스는 내부적으로 버퍼(buffer)라고 하는 독립적인 공간을 가진다
버퍼 크기의 기본값은 16개의 문자를 저장할 수 있는 크기이며, 생성자를 통해 그 크기를 별도로 설정할 수도 있다
String 클래스와 같은 불변 클래스는 StringBuffer 클래스의 append()나 insert() 메소드와 같이 값을 변경하는 set 메소드를 포함하지 않는다
그러면 StringBuffer쓰지 String 같은 불변 클래스를 왜쓰냐?
멀티 스레드 환경에서 객체가 변화되는 상황이라면 불변 인스턴스를 사용하는 것이 좀 더 신뢰할 수 있는 코드를 작성할 수 있기 때문
- 하나의 객체에 접근하면서 각각의 객체가 서로 영향을 주어서는 안 되는 경우에 불변 인스턴스를 사용하면 값이 변하지 않는다는 점이 보장된다
append() 메소드
인수로 전달된 값을 문자열로 변환한 후, 해당 문자열의 마지막에 추가한다
이 메소드는 String 클래스의 concat() 메소드와 같은 결과를 반환하지만, 내부적인 처리 속도가 훨씬 빠르다
StringBuffer str = new StringBuffer("Java");
System.out.println("원본 문자열 : " + str);
System.out.println(str.append("수업"));
System.out.println("append() 메소드 호출 후 원본 문자열 : " + str);
실행결과:
원본 문자열 : Java
Java수업
append() 메소드 호출 후 원본 문자열 : Java수업
capacity() 메소드
StringBuffer 인스턴스의 현재 버퍼 크기를 반환
StringBuffer str01 = new StringBuffer();
StringBuffer str02 = new StringBuffer("Java");
System.out.println(str01.capacity());
System.out.println(str02.capacity());
실행결과:
16
20
delete() 메소드
전달된 인덱스에 해당하는 부분 문자열을 해당 문자열에서 제거한다
deleteCharAt() 메소드를 사용하면 특정 위치의 문자 한 개만을 제거할 수도 있다
StringBuffer str = new StringBuffer("Java Oracle");
System.out.println("원본 문자열 : " + str);
System.out.println(str.delete(4, 8)); // 0~7까지 제거
System.out.println(str.deleteCharAt(1));
System.out.println("deleteCharAt() 메소드 호출 후 원본 문자열 : " + str);
실행결과:
원본 문자열 : Java Oracle
Javacle
Jvacle
deleteCharAt() 메소드 호출 후 원본 문자열 : Jvacle
insert() 메소드
인수로 전달된 값을 문자열로 변환한 후, 해당 문자열의 지정된 인덱스 위치에 추가한다
- 전달된 인덱스가 해당 문자열의 길이와 같으면, append() 메소드와 같은 결과를 반환한다
StringBuffer str = new StringBuffer("Java 만세!!");
System.out.println("원본 문자열 : " + str);
System.out.println(str.insert(4, "Script"));
System.out.println("insert() 메소드 호출 후 원본 문자열 : " + str);
원본 문자열 : Java 만세!!
JavaScript 만세!!
insert() 메소드 호출 후 원본 문자열 : JavaScript 만세!!
reverse() 메소드
해당 문자열의 인덱스를 역순으로 재배열함
위에서 본것 처럼 String instance는 메소드를 적용하고 나서도 원본 값은 바뀌지 않았지만 StringBuffer instance는 메소드를 적용하니 원본값도 바뀐것을 알 수 있다