1. java.lnag패키지
1.1 Object클래스
Object클래스는 멤버변수는 없고 오직 11개의 메서드만 가지고 있다.
Object클래스의 메서드 | 설명 |
protected Object clone() | 객체 자신의 복사본을 반환한다. |
public boolean equals(Object obj) | 호출한 객체 자신과 obj가 같은 객체라면 true, 아니면 false |
protected void finalize() | 객체가 소멸될 때 가비지 컬렉터에 의해 자동으로 호출된다. 이 때 수행되어야하는 코드가 있을 때 오버라이딩한다.(거의 사용안함) |
public Class getClass() | 객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환한다. |
public int hashCode() | 객체 자신의 해시코드를 반환한다. |
public String toString() | 객체 자신의 정보를 문자열로 반환한다. |
public void notify() | 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다. |
public void notifyAll() | 객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨운다. |
public void wait() public void wait(long timeout) public void wait(long timeout, int nanos) |
다른 쓰레드가 notify()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게 한다. (timeout은 1/1000초, nanos는 1/10의 9승초) |
equals(Object obj)
매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean값으로 알려주는 역할.
두 객체의 같고 다름을 참조변수의 값으로 판단한다. 그래서 서로 다른 두 객체를 equals로 비교하면 false를 결과로 얻게 된다.
특정 클래스의 객체끼리 같은지를 비교하고 싶다면 equals메서드를 오버라이딩하여 객체에 저장된 내용을 비교하도록 변경하면 true값을 얻을 수 있다. equals메서드를 Persond인스턴스의 주소값이 아닌 멤버변수 id의 값을 비교하게 오버라이딩했다. == 연산자는 두 값의 주소값을 비교하기 때문에 false로 결과가 나타난 것이다.
class Person() {
long id;
public boolean equals(Object obj) {// Object의 equals() 오버라이딩
if(obj instanceof Person) {
return id == ((Person) obj).id;
} else {
return false;
}
}
}
public class Example {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
p1.id = 31414123123l;
p2.id = 31414123123l;
System.out.println(p1.equals(p2)); // true 출력
System.out.println(p1 == p2); // false 출력
}
}
hashCode()
해싱(hashing)기법에 사용되는 해시함수(hash function)을 구현한 메서드. 해싱은 데이터 관리 기법 중 하나로, 다량의 데이터를 저장하고 검색하는 데 유용하다. 해시함수는 찾고자하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드(hash code)를 반환한다.
64bit JVM에서는 8byte 주소값으로 해시코드(4byte)를 만들기 때문에 해시코드가 중복될 수 있다. 그러므로 해시코드가 같은 두 객체가 존재하는 것이 가능하다.
- 클래스의 인스턴스 변수 값으로 객체의 같고 다름을 판단해야 하는 경우라면 equals메서드와 hashCode메서드를 적절히 오버라이딩해야 한다. 같은 객체라면 hashCode메서드를 호출했을 때 결과값인 해시코드도 같아야 하기 때문이다.
- System.identityHashCode(Object x)는 객체의 주소값으로 해시코드를 생성하기 때문에 모든 객체에 대해 항상 다른 해시코드값을 반환한다.
toString()
인스턴스에 대한 정보를 문자열로 제공할 수 있도록 정의한 메서드. 인스턴스의 정보를 제공한다는 것은 대부분의 경우 인스턴스 변수에 저장된 값들을 문자로 표현한다는 뜻이다.
Object클래스에 정의된 toString()은 클래스명에 16진수의 해시코드를 반환한다.
public String toString() {
return getClass().getName()+"@"Integer.toHexString(hashCode());
}
equals메서드와 마찬가지로 toString메서드도 특정 클래스에 맞게 오버라이딩할 수 있다. toString()은 일반적으로 인스턴스나 클래스에 대한 정보 또는 인스턴스 변수들의 값을 문자열로 변환하여 반환하도록 오버라이딩되는 것이 보통이다. Object클래스에 정의된 toString()이 public이므로 오버라이딩할 때에도 public으로 선언해야 한다. 조상에 정의된 메서드를 자손에서 오버라이딩할 때는 조상에 정의된 접근범위보다 같거나 더 넓어야 하기 때문이다.
clone()
자신을 복제하여 새로운 인스턴스를 생성. 어떤 인스턴스에 대해 작업을 할 때, 원래 인스턴스는 보존하고 clone()을 이용해 새 인스턴스를 생성하여 작업하면 실패해도 원래대로 돌아갈 수 있다.
Object클래스에 정의된 clone()은 인스턴스 변수의 값만 복사하기 때문에 참조타입의 인스턴스 변수가 있는 클래스는 완전한 인스턴스 복제가 이뤄지지 않으므로 오버라이딩으로 재정의 해야한다.
clone()을 사용하려면
1. 복제할 클래스가 Cloneable 인터페이스를 꼭 구현하고 (인스턴스가 복제될 때 인스턴스의 데이터를 보호하기 위해서)
2. clone()을 오버라이딩하면서 접근 제어자를 protected에서 public으로 변경한다.
그래야만 상속관계가 없는 다른 클래스에서 clone()을 호출할 수 있다.
3. 조상클래스의 clone()을 호출하는 코드가 포함된 try-catch문을 작성한다.
공변 반환타입
오버라이딩할 때 조상 메서드의 반환타입을 자손 클래스의 타입으로 변경을 허용하는 기능(JDK1.5~)
class Point {
...
public Point clone() { // 1. 반환 타입을 Object에서 Point로 변경
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
}
return (Point) obj; // 2. Point타입으로 형변환
}
}
이렇게 공변반환타입을 이용하면 조상의 타입이 아닌 실제로 반환되는 자손 객체의 타입으로 반환할 수 있어서 번거로운 형변환이 줄어든다.
아래 코드처럼 배열을 복사할 때 사용할 수 있고 이 밖에도 Vector, ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, Calendar, Date클래스들도 이처럼 복사가 가능하다.
int[] arr = { 1, 2, 3, 4, 5 };
int[] arrClone = arr.clone();
// 위 아래 코드는 같은 결과를 얻는다.
int[] arrClone2 = new int[arr.length];
System.arraycopy(arr, 0, arrClone2, 0, arr.length);
얕은 복사와 깊은 복사
clone()은 단순히 객체에 저장된 값을 그대로 복제할 뿐, 객체가 참조하고 있는 객체까지 복제하지는 않는다. clone()으로 복제하면 원본과 복제본이 같은 객체를 공유하므로 완전한 복제라고 보기 어렵다. 이러한 복제를 얕은 복제(shallow copy)라고 한다.
반면 원본이 참조하고 있는 객체까지 복제하는 것을 깊은 복사(deep copy)라고 한다. 깊은 복사에서는 원본과 복사본이 서로 다른 객체를 참조하기 때문에 원본의 변경이 복사본에 영향을 미치지 않는다.
class Circle implements Cloneable {
Point p;
double r;
Circle(Point p, double r) {
this.p = p;
this.r = r;
}
public Circle shallowCopy() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
}
return (Circle) obj;
}
public Circle deepCopy() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
}
Circle c = (Circle) obj;
c.p = new Point(this.p.x, this.p.y);
return c;
}
}
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Example {
public static void main(String[] args) {
Circle c1 = new Circle(new Point(1,1), 2.0);
Circle c2 = c1.shallowCopy();
Circle c3 = c1.deepCopy();
}
}
위 코드를 그림으로 표현하면 이렇다.
c2는 Object의 clone()을 호출해 얕은 복사 즉, 원본 객체가 가지고 있는 값만 그대로 복사하고 c3는 복제된 객체가 새로운 Point 인스턴스를 참조하도록 했다. 즉 원본이 참조하고 있는 객체까지 복사한 것이다.
getClass()
자신이 속한 클래스의 Class 객체를 반환하는 메서드로, Class 객체는 이름이 Class인 클래스의 객체이다. Class 클래스는 클래스의 모든 정보를 담고 있으며, 클래스 당 한개만 존재한다. 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때 자동으로 Class 객체가 생성된다.
클래스 로더는 실행 시에 필요한 클래스를 동적으로 메모리에 로드하는 역할을 한다. 먼저 기존에 생성된 클래스 객체가 메로리에 존재하는지 확인하고, 있으면 객체의 참조를 반환하고 없으면 클래스 패스(classpath)에 지정된 경로를 따라서 클래스 파일을 찾는다. 못 찾으면 ClassNotFoundException이 발생하고 찾으면 해당 클래스 파일을 읽어서 Class객체로 변환한다. 그러니까 파일 형태로 저장되어 있는 클래스를 읽어서 Class클래스에 정의된 형식으로 변환하는 것이다. 클래스 파일을 읽어서 사용하기 편한 형태로 저장해 놓은 것이 클래스 객체이다.
Class객체를 얻는 방법
클래스의 정보가 필요할 때 Class객체에 대한 참조를 얻어 와야 하는데 이에 대한 방법은 대표적으로 세가지가 있다.
Class obj = new Card().getClass(); // 생성된 객체로부터 얻는 방법
Class obj = Card.class; // 클래스 리터럴(*.class)로부터 얻는 방법
Class obj = Class.forName("Card"); // 클래스의 이름으로부터 얻는 방법. 특정 클래스 파일을 메모리에 올릴 때 사용
Class객체를 이용하면 클래스에 정의된 멤버의 이름이나 개수 등의 정보를 얻을 수 있기 때문에 Class객체를 통해 객체를 생성하고 메서드를 호출하는 등 보다 동적인 코드를 작성할 수 있다.
1.2 String클래스
자바에서 제공하는 문자열을 다루는 클래스로, 문자열을 저장하고 이를 다루는데 필요한 메서드도 제공한다.
변경 불가능한(immutable) 클래스
한번 생성된 String인스턴스가 갖고 있는 문자열은 읽어올 수만 있고, 변경할 수는 없다. 만약 '+'연산자를 이용해 문자열을 결합하는 경우 인스턴스 내의 문자열이 바뀌는 것이 아니라 새로운 문자열이 담긴 String인스턴스가 생성되는 것이다. 이렇게 문자열을 결합하면 연산 시마다 새로운 문자열을 가지는 String인스턴스가 생성되어 메모리공간을 차지하므로 결합횟수를 줄이는 것이 좋다.
문자열간의 추출이나 결합 등 문자열을 다루는 작업이 많이 필요할 경우 StringBuffer클래스를 사용하는 것이 좋다. StringBuffer인스턴스에 저장된 문자열은 변경이 가능하므로 하나의 인스턴스만으로도 문자열을 다룰 수 있다.
문자열의 비교
문자열을 생성하는 방법은 문자열 리터럴을 지정하는 방법과 String클래스의 생성자를 사용하는 방법 두가지가 있다.
String str1 = "abc"; // 문자열 리터럴 abc의 주소가 str1에 저장됨
String str2 = "abc"; // 위와 동일
String str3 = new String("abc"); // 새로운 String인스턴스 생성
String str4 = new String("abc"); // 위와 동일
str3, str4와 같은 경우 new연산자에 의해 메모리할당이 이루어지기 때문에 항상 새로운 String인스턴스가 생성된다. 하지만 문자열 리터럴(str1, str2)은 이미 존재하는 것을 재사용하는 방식이다. str3과 str4는 엄연히 다른 객체이기 때문에 ==로 비교하면 false를 결과로 얻는다.
문자열 리터럴
자바 소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다. 이때 같은 내용의 문자열 리터럴은 한번만 저장된다. 문자열 리터럴도 String인스턴스고, 한번 생성하면 내용을 변경할 수 없으니 하나의 인스턴스를 공유하면 되기 때문이다.
빈 문자열(empty string)
@: 찰리찰리. 길이가 0인 배열이 존재할 수 있어?
찰리찰리: Y..es...
String s = "";와 같은 문장이 있을 때, s가 참조하고 있는 String인스턴스는 내부에 new char[0]과 같이 길이가 0인 char 배열을 저장하고 있는 것이다.
C에서는 길이가 0인 배열을 선언할 수 없다.
String s = "";가 가능하다고 해서 char c = '';가 가능하지는 않다. char형 변수에는 반드시 하나의 문자를 지정해야 한다.
String클래스의 생성자와 메서드
메서드 | 설명 | 예제 | 결과 |
String(String s) | 주어진 문자열(s)을 갖는 String인스턴스를 생성한다. | String s = new String("Hi"); | s = Hi |
String(char[] value) | 주어진 문자열(value)를 갖는 String인스턴스를 생성한다. | char[] c = { 'H', 'i' }; String s = new String(c); |
s = Hi |
String(StringBuffer buf) | StringBuffer인스턴스가 갖고 있는 문자열과 같은 내용의 String인스턴스를 생성한다. | StringBuffer sb = new StringBuffer("Hi"); String s = new String(sb); |
s = Hi |
char charAt(int index) | 지정된 위치(index)에 있는 문자를 알려준다. (index는 0부터 셈) | String s = "Hi"; char c = s.charAt(1); |
c = i |
int compareTo(String str) | 문자열(str)과 사전순서로 비교해 같으면 0, 사전순으로 이전이면 음수, 이후면 양수를 반환한다. | int i = "b".compareTo("b"); int j = "b".compareTo("a"); int k = "b".compareTo("c"); |
i = 0 j = 1 k = -1 |
String concat(String str) | 문자열(str)을 뒤에 덧붙인다. | String s = "H"; String s2 = s.concat("i"); |
s2 = Hi |
boolean contains(CharSequence s) | 지정된 문자열(s)이 포함되었는지검사한다. 하나라도 포함되면 true 반환 | String s = "Hi"; boolean b = s.contains("Hi"); |
b = true |
boolean endsWith(String suffix) | 지정된 문자열(suffix)로 끝나는면 true, 아니면 false | String s = "Hi"; boolean b = s.endsWith("i"); |
b = true |
boolean equals(Object obj) | obj와 String인스턴스의 문자열을 비교해 obj가 String이 아니거나 값이 다르면 false반환 | String s = "Hi"; boolean b = s.equals("Hi"); |
b = true |
boolean equalsIgnoreCase(String str) | str과 String인스턴스의 문자열을대소문 없이 구분해 같으면 true | String s = "Hi"; boolean b = s.equalsIgnoreCase("hI"); |
b = true |
int indexOf(int ch) | 주어진 문자(ch)가 문자열에 존재하는지 확인해 위치(index)를 알려줌. 못 찾으면 -1 반환 | String s = "Hi"; int idx = s.indexOf('i'); |
idx = 1 |
int indexOf(int ch, int pos) | 주어진 문자(ch)가 문자열에 존재하는지 지정된 위치(pos)부터 확인하여 위치(index)를 알려준다. 못 찾으면 -1 반환 | String s = "Hello"; int idx = s.indexOf('l', 1); |
idx = 2 |
int indexOf(String str) | 주어진 문자열이 존재하는지 확인하여 그 위치(index)를 알려준다. 없으면 -1을 반환. | String s = "Hello"; int idx = s.indexOf("ll"); |
idx = 2 |
String intern() | 문자열을 상수풀(constant pool)에 등록한다. 이미 상수풀에 같은내용의 문자열이 있을 경우 그 문자열의 주소값을 반환한다. | String s = new String("abc"); String s2 = new String("abc"); boolean b = (s.intern() == s2.intern()); |
b = true |
int lastIndexOf(int ch) | 지정된 문자 또는 문자코드를 문자열의 오른쪽 끝에서부터 찾아서 위치(index)를 알려준다. 못 찾으면 -1 반환 | String s = "java.lang."; int idx1 = s.lastIndexOf('.'); int idx2 = s.IndexOf('.'); |
idx1 = 9 idx2 = 4 |
int lastIndexOf(String str) | 지정된 문자열을 인스턴스의 문자열 끝에서부터 찾아서 위치(index)를 알려준다. 못 찾으면 -1 반환 | String s = "java.l.java"; int idx1 = s.lastIndexOf("java"); |
idx1 = 7 |
int length() | 문자열의 길이를 알려준다. | String s = "Hi"; int len = s.length(); |
len = 2 |
String replace(char old, charnw) | 문자열의 old문자를 new문자로 바꾼 문자열을 반환 | String s = "Hi"; String s1 = s.replace('i', 'o'); |
s1 = Ho |
String replace(CharSequence old, CharSequence nw) | 문자열의 old문자열을 new문자열로 바꾼 문자열을 반환 | String s = "Hello"; String s2 = s.replace("ll", "ww"); |
s2 = Hewwo |
String replaceAll(String regex, String replacement) | 문자열에서 지정된 문자열(regex)과 일치하면 새로운 문자열(replacement)로 모두 변경한다. | String s = "aabbaabb"; String s2 = s.replaceAll("aa", "AA"); |
s2 = AAbbAA |
String replaceFirst(String regex, String replacement) | 문자열에서 지정된 문자열(regex)과 일치하는 첫 문자열만 새로운 문자열(replacement)로 변경한다. | String s = "aabbaa"; String s2 = s.replaceFirst("aa", "AA"); |
s2 = AAbbaa |
String[] split(String regex) | 문자열을 지정된 분리자(regex)로 나누어 문자열 배열에 담아 반환한다. | String alphabet = "a, b, c"; String s = alphabet.split(","); |
s[0] = "a" s[1] = "b" s[2] = "c" |
String[] split(String regex, int limit) | 문자열을 지정된 분리자(regex)로 나누어 문자열배열에 담아 반환한다. 단 문자열 전체를 지정된 수(limit)로 자른다. | String alphabet = "a, b, c" String s = alphabet.split(",", 2); |
s[0] = "a" s[1] = "b, c" |
boolean startsWith(String prefix) | 주어진 문자열(prefix)로 시작하는지 검사한다. | String s = "java.lang"; boolean b = s.startsWith("java"); |
b = true |
String substring(int begin) String substring(int begin, int end) |
주어진 위치(begin)부터 끝(end)까지 포함된 문자열을 얻는다. begin <= x < end |
String s = "Hello"; String s2 = s.substring(4); String s3 = s.substring(1, 4); |
s2 = o s3 = ell |
String toLowerCase() | String인스턴스에 저장되어 있는 모든 문자열을 소문자로 변환하여 반환한다. | String s = "HI"; String s2 = s.toLowerCase(); |
s2 = hi |
String toString() | String인스턴스에 저장되어 있는 문자열을 반환한다. | String s = "HI"; String s2 = s.toString(); |
s2 = HI |
String toUpperCase() | String인스턴스에 저장되어 있는 모든 문자열을 대문자로 변환하여 반환한다. | String s = "HEllo"; String s2 = s.toUpperCase(); |
s2 = HELLO |
String trim() | 문자열의 좌우 끝에 있는 공백을 없앤 결과를 반환한다. | String s = " a a "; String s2 = s.trim(); |
s2 = a a |
static String valueOf(boolean b) static String valueOf(char c) static String valueOf(int i) static String valueOf(long l) static String valueOf(float f) static String valueOf(double d) static String valueOf(Object o) |
지정된 값을 문자열로 변환하여 반환한다. 참조변수의 경우 toString()을 호출한 결과를 반환한다. | String b = String.valueOf(true); String c = String.valueOf('c'); String i = String.valueOf(4); String l = String.valueOf(3l); String f = String.valueOf(2.0f); String d = String.valueOf(6.5); String date = String.valueOf(new Date()); |
b = true c = c i = 4 l = 3 f = 2.0 d = 6.5 date = "Mon Mar 27 21:21:30 KST 2023" |
join()과 StringJoiner
join()은 여러 문자열 사이에 구분자를 넣어서 결합한다. ⇔ split()
StringJoiner클래스도 같은 역할을 한다.
StringJoiner sj = new StringJoiner(",", "[", "]");
String[] strArr = { "aaa", "bbb", "ccc" };
for (String s : strArr) {
sj.add(s);
}
System.out.println(sj.toString()); // 결과: [aaa, bbb, ccc]
유니코드의 보충문자
유니코드는 원래 16비트 문자체계인데 이걸로 모자라서 20비트로 확장하게 되었다. 그래서 하나의 문자를 char타입으로 다루지 못하고 int타입으로 다루게 되었다. 확장에 의해 새로 추가된 문자들을 '보충 문자'라고 한다. String의 메서드 중 매개변수가 'int ch'인 것들이 보충문자를 지원하고, 'char ch'인 것들은 지원하지 않는 것들이다.
문자 인코딩 변환
getBytes(String charsetName)를 사용하면, 문자열의 인코딩을 다른 인코딩으로 변경할 수 있다. 자바가 UTF-16을 사용하지만, 문자열 리터럴에 포함되는 문자들은 OS의 인코딩을 사용한다. 한글 윈도우즈의 경우 문자 인코딩으로 CP949(=MS949)를 사용하며, UTF-8로 변경하려면, 아래처럼 한다.
byte[] utf8_str = "가".getBytes("UTF-8");
String str = new String(utf8_str, "UTF-8");
서로 다른 문자 인코딩을 사용하는 컴퓨터 간에 데이터를 주고받을 때는 적절한 문자 인코딩이 필요하다. UTF-8은 한글 한 글자를 3byte로 표현하고, CP949는 2byte로 표현한다.
String.format()
형식화된 문자열을 만들어내는 간단한 방법이다. prinf()와 방법이 완전히 같다.
기본형 값을 String으로 변환
- 기본형 + ""
- String.valueOf(기본형 매개변수)
성능은 valueOf()가 더 좋지만 빈 문자열을 더하는 방법이 간단하고 편하기 때문에 성능향상이 필요할 경우 valueOf()를 사용하자.
String을 기본형 값으로 변환
1. 래퍼클래스.valueOf(String str)
2. 래퍼클래스.parse타입(String str)
각 래퍼클래스의 valueOf(String s)는 메서드 내부에서 그저 parse메서드를 호출할 뿐이므로 두 메서드는 반환타입만 다르지 같은 메서드다. parseInt()나 parseFloat()같은 메서드는 문자열에 공백 또는 문자가 포함되어 있는 경우 변환 시 예외가 발생할 수 있으므로 주의해야 한다. 그러나 부호를 의미하는 '+'나 소수점을 의미하는 '.', float값을 의미하는 'f'와 같은 자료형 접미사는 자료형에 알맞은 변환을 하는 경우에만 허용된다.
1.3 StringBuffer클래스와 StringBuilder클래스
String클래스는 인스턴스를 생성할 때 지정된 문자열을 변경할 수 없지만 StringBuffer클래스는 변경이 가능하다. 내부적으로 문자열 편집을 위한 버퍼를 가지고 있으며 StringBuffer인스턴스를 생성할 때 그 크기를 지정할 수 있다. 이 때, 편집할 문자열의 길이를 고려하여 버퍼의 길이를 충분히 잡아주는 것이 좋다. 편집 중인 문자열이 버퍼의 길이를 넘어서게 되면 버퍼의 길이를 늘려주는 작업이 추가로 수행되어야 하기 때문에 작업 효율이 떨어진다.
StringBuffer의 생성자
StringBuffer클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편집하기 위한 공간(buffer)로 사용된다.
public StringBuffer(int length) { // 버퍼의 크기를 지정
value = new char[length];
shared = false;
}
public StringBuffer() { // 버퍼의 크기를 지정하지 않으면 16으로 지정함.
this(16);
}
public StringBuffer(String str) { // 지정한 문자열 길이보다 16 더 크게 버퍼를 생성한다.
this(str.length() + 16);
append(str)
}
StringBuffer인스턴스로 문자열을 다루는 작업을 할 때, 버퍼의 크기가 작업하려는 문자열의 길이보다 작을 때는 내부적으로 버퍼의 크기를 증가시키는 작업이 수행된다. 배열의 길ㅇ는 변경될 수 없으므로 새로운 길이의 배열을 생성한 후에 이전 배열의 값을 복사해야한다.
StringBuffer의 변경
StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = sb.append("123");
위 작업을 수행하면 sb의 값은 abc였다가 append()로 인해 abc123이 된다. 해당 값을 sb2에 할당하면 sb2도 abc123이 된다. append()는 반환 타입이 StringBuffer인데 자신의 주소를 반환한다. 위 문장을 수행하면 sb에 새로운 문자열 123이 추가되고 sb 자신의 주소를 반환하여 sb2에는 sb의 주소가 저장된다. 즉, sb와 sb2가 모두 같은 StringBuffer인스턴스를 가리키고 있으므로 같은 내용이 출력된다.
StringBuffer의 비교
StringBuffer클래스는 equals()를 오버라이딩 하지 않아서 equals메서드를 사용해도 ==로 비교한 것과 같은 결과를 얻는다.
반면 toString()은 오버라이딩 되어 있어서 StringBuffer인스턴스에 toString()을 호출하면 담고 있는 문자열을 String으로 반환한다. 그래서 StringBuffer인스턴스에 담긴 문자열을 비교하기 위해서는 StringBuffer인스턴스에 toString()을 호출 → String 인스턴스 얻음 → equals메서드를 사용한다
String s = sb.toString();
String s2 = sb2.toString();
System.out.println(s.equals(s2)); // true
StringBuffer클래스의 생성자와 메서드
메서드 | 설명 | 예제 | 결과 |
StringBuffer() | 16문자를 담을 수 있는 버퍼를 가진 StringBuffer인스턴스 생성 | StringBuffer sb = new StringBuffer(); | |
StringBuffer(int length) | 지정된 개수(length)의 문자를 담을 수 있는 버퍼를 가지는 StringBuffer인스턴스 생성 | StringBuffer sb = new StringBuffer(10); | |
StringBuffer(String str) | 지정된 문자열(str)을 값으로 가지는 StringBuffer인스턴스 생성 | StringBuffer sb = new StringBuffer("Hi"); | sb = Hi |
StringBuffer append(boolean b) StringBuffer append(char c) StringBuffer append(char[] c) StringBuffer append(int i) StringBuffer append(long l) StringBuffer append(float f) StringBuffer append(double d) StringBuffer append(Object obj) StringBuffer append(String str) |
매개변수로 입력된 값을 문자열로 변환하여 StringBuffer인스턴스가 저장하고 있는 문자열의 뒤에 덧붙인다. | ||
int capacity() | StringBuffer인스턴스의 버퍼크기를 알려준다. | StringBuffer sb = new StringBuffer(100); int bufSize = sb.capacity(sb); |
bufSize = 100 |
char charAt(int index) | 지정된 위치(index)에 있는 문자를 반환한다. | StringBuffer sb = new StringBuffer("abc"); char c = sb.charAt(1); |
c = b |
StringBuffer delete(int start, int end) | start부터 end사이에 있는 문자를 제거한다. start <= sb < end |
StringBuffer sb = new StringBuffer("0123456"); sb.delete(3, 6); |
sb = 0126 |
StringBuffer deleteCharAt(int index) | 지정된 위치(index)의 문자를 제거한다. | StringBuffer sb = new StringBuffer("0123"); sb.deleteCharAt(3); |
sb = 012 |
StringBuffer insert(int pos, boolean b) StringBuffer insert(int pos, char c) StringBuffer insert(int pos, char[] c) StringBuffer insert(int pos, int i) StringBuffer insert(int pos, long l) StringBuffer insert(int pos, float f) StringBuffer insert(int pos, double d) StringBuffer insert(int pos, Object obj) StringBuffer insert(int pos, String str) |
두 번째 매개변수로 받은 값을 문자열로 변환하여 지정된 위치(pos)에 추가한다. pos는 0부터 시작 | StringBuffer sb = new StringBuffer("0123456"); sb.insert(4, '.'); |
sb = 0123.456 |
int length() | StringBuffer인스턴스에 저장되어 있는 문자열의 길이를 반환한다. | StringBuffer sb = new StringBuffer("0123456"); int len = sb.length(); |
len = 7 |
StringBuffer replace(int start, int end, String str) | start~end의 문자들을 주어진 문자열로 바꾼다. start<= sb < end |
StringBuffer sb = new StringBuffer("0123456"); sb.replace(3, 6, "ab"); |
sb = 012ab6 |
StringBuffer reverse() | StringBuffer인스턴스에 저장되어 있는 문자열의 순서를 거꾸로 나열한다. | StringBuffer sb = new StringBuffer("0123456"); sb.reverse() |
sb = 6543210 |
void setCharAt(int index, char ch) | 지정된 위치의 문자를 주어진 문자(ch)로 바꾼다. | StringBuffer sb = new StringBuffer("0123456"); sb.setCharAt(5, 'o'); |
sb = 01234o6 |
void setLength(int newLength) | 지정된 길이로 문자열의 길이를 변경한다. 길이를 늘리는 경우에 나머지 빈공간을 널문자'\u0000'로 채운다. | StringBuffer sb = new StringBuffer("0123456"); sb.setLength(5); sb.setLength(10); |
0123456 0123456___ |
String toString() | StringBuffer인스턴스의 문자열을 String으로 반환 | ||
String substring(int start) String substring(int start, int end) |
지정된 범위 내의 문자열을 String으로 뽑아서 반환한다. start만 있는 메서드는 문자열의 끝까지 뽑아낸다. |
StringBuilder란?
StringBuffer는 멀티스레드에 안전(thread safe)하도록 동기화되어 있다. 동기화는 성능을 떨어뜨린다. 멀티스레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 불필요하게 성능만 떨어뜨리게 된다.
StringBuilder는 StringBuffer의 동기화를 뺀 클레스로 기능은 완전히 같다.
1.4 Math클래스
Math클래스는 기본적인 수학계산에 유용한 메서드로 구성되어 있다. Math클래스의 생성자는 private이기 때문에 다른 클래스에서 Math인스턴스를 생성할 수 없도록 되어있다. Math클래스의 메서드는 모두 static이고 두 상수 E와 PI만 정의되어 있기 때문이다. (인스턴스 멤버 無)
올림, 버림, 반올림
소숫점 n번째 자리에서 반올림한 값을 얻으려면 round()를 사용한다. round()는 항상 소수점 첫재짜리에서 반올림을 해서 정수값(long)을 결과로 돌려준다.
만약 원하는 자리에서 반올림된 값을 얻기 위해서는 간단히 10의 n제곱으로 곱한 후, 다시 곱한 수로 나눠주면 된다.
90.7552를 소숫점 셋째자리에서 반올림하고 싶으면 이 값에 100(10^자리-1)을 곱해 9075.52 여기에 round()를 사용하면 9076이 되고 여기에 100.0(실수)을 나누면 90.76이 된다.
rint()도 round()처럼 소수점 첫째자리에서 반올림하지만 double로 반환한다. 그리고 두 정수 정가운데에 있는 가장 가까운 짝수 정수를 반환한다. rint(-1.5)는 -2.0을 반환한다. -1.5가 -1.0과 -2.0의 딱 중간값이고 그 중 짝수가 -2.0이기 때문이다. round(-1.5)는 더 큰 값으로 반올림하므로 -1을 반환한다.
예외를 발생시키는 메서드
Math클래스에 메서드 이름에 Exact가 포함된 메서드가 있다.(jdk 1.8~) 이들은 정수형 간의 연산에서 발생할 수 있는 오버플로우를 감지하기 위해 존재한다.
int addExact(int x, int y);
int subtractExact(int x, int y);
int multiplyExact(int x, int y);
int incrementExact(int a); // a++
int decrementExact(int a); // a--
int negateExact(int a); // -a
int toIntExact(long value); // int로 형변환
연산자는 단지 결과를 반환할 뿐, 오버플로우의 발생여부에 대해 알려주지 않는다. 그러나 위 메서드들은 오버플로우가 발생하면 예외(ArithmeticException)를 발생시킨다.
negateExact(int a)는 매개변수의 부호를 반대로 바꿔주는데 무슨 예외가 발생할까? 부호를 반대로 바꾸는 식은 '~a+1'이다. '~a'의 결과가 int의 최대값일 경우 여기에 1을 더하니까 오버플로우가 발생할 수 있는 것이다.
삼각함수와 지수, 로그
// √(x2-x1)² + (y2-y2)²을 sqrt()와 pow()로 표현하기
double c = sqrt(pow(x2-x1, 2) + pow(y2-y1, 2));
- squrt(double a): 제곱근을 double로 반환
- pow(double a, double b): a를 b제곱함
double a = c * sin(PI/4); // c: 빗변 길이
double b = c * cos(PI/4);
// 위아래 코드는 같은 의미
double b = c * cos(toRadians(45)); // 각도 -> 라디안으로 바꿈
삼각함수는 매개변수의 단위가 라디안이다. 180° = π 라디안이므로 45도를 라디안 단위로 바꾸면 PI/4가 된다.
- toRadians(double angdeg): degree를 라디안 단위로 바꿔 double로 반환한다.
System.out.println("angle = %f rad", atan2(a, b));
- atan2(double a, double b): 직각 삼각형에서 두 변의 길이 a, b를 알면 끼인각을 라디안 단위로 알려준다.
- toDegrees(double angrad): 라디안 값을 각도 단위로 바꿔 double로 반환한다.
System.out.printf(24 * log10(2)); // 7.224720
- log10(double a): 밑이 10이고 진수가 a인 값을 반환한다.
StrictMath클래스
Math클래스는 최대한의 성능을 얻기 위해 JVM이 설치된 OS의 메서드를 호출해서 사용한다. 즉, OS에 의존적인 계산을 한다. 예를 들어 부동소수점 계산의 경우, 반올림의 처리방법 설정이 OS마다 다를 수 있기 때문에 자바로 작성된 프로그램임에도 불구하고 컴퓨터마다 결과가 다를 수 있다. 이러한 차이를 없애기 위해 성능은 다소 포기하는 대신, 어떤 OS에서 실행되어도 항상 같은 결과를 얻도록 Math클래스를 새로 작성한 것이 StrictMath클래스이다.
Math클래스의 메서드
※ 전부 static이므로 static 생략
메서드 | 설명 | 예제 | 결과 |
double abs(double a) float abs(float f) int abs(int f) long abs(long f) |
주어진 값의 절댓값을 반환한다. | int i = Math.abs(-10); | i = 10 |
double ceil(double a) | 주어진 값을 올림하여 반환한다. | double d= Math.ceil(10.1); | d = 11 |
double floor(double a) | 주어진 값을 버림하여 반환한다. | double d = Math.floor(10.1); | d = 10 |
double max(double a, double b) float max(float a, float b) int max(int a, int b) long max(long a, long b) |
주어진 두 값을 비교하여 큰 쪽을 반환한다. | float max = Math.max(-1.0f, 1.0f) | max = 1.0 |
double min(double a, double b) float min(float a, float b) int min(int a, int b) long min(long a, long b) |
주어진 두 값을 비교하여 작은 쪽을 반환한다. | float min = Math.min(-1.0f, 1.0f) | min = -1.0 |
double random() | 0.0 <= 임의의 double값 <1.0을 반환한다. | double ran = Math.random(); | |
double rint(double a) | 주어진 double값과 가장 가까운 정수값을 double형으로 반환한다. 단, 두 정수의 가운데 있는 값(1.5 등)은 짝수를 반환 | double d = Math.rint(1.2); double d2 = Math.rint(1.5); double d3 = Math.rint(2.5); |
d = 1.0 d2 = 2.0 d3 = 2.0 |
long round(double a) long round(float a) |
소수점 첫째자리에서 반올림한 정수값을 long타입으로 반환한다. | long l = Math.round(1.2); long l2 = Math.round(1.5); long l3 = Math.round(2.5); |
l = 1 l2 = 2 l3 = 3 |
1.5 래퍼(Wrapper) 클래스
객체지향 개념에서 모든 것은 객체로 다뤄져야 한다. 그러나 자바에서는 기본형을 객체로 다루지 않는데, 때로는 기본형 변수도 어쩔 수 없이 객체로 다뤄야 하는 경우가 있다. 예를 들면, 매개변수로 객체를 요구할 때, 기본형 값이 아닌 객체로 저장해야할 때, 객체 간의 비교가 필요할 때 등등이다. 이럴 때 사용되는 것이 래퍼 클래스이다. 8개의 기본형을 대표하는 8개의 래퍼클래스가 있는데, 이 클래스들을 이용하면 기본형 값을 객체로 다룰 수 있다.
래퍼 클래스들은 모두 equals()가 오버라이딩되어 있어서 주소값이 아닌 객체가 가지고 있는 값을 비교한다. Integer객체에 비교 연산자를 사용할 수 없다. 대신 compareTo()를 제공한다. 그리고 toString()도 오버라이딩 되어 있어서 객체가 가지고 있는 값을 문자열로 변환하여 반환한다. 이 외에도 래퍼 클래스들은 MAX_VALUE, MIN_VALUE, SIZE, BYTES, TYPE 등의 상수를 공통적으로 가진다.
기본형 | 래퍼클래스 | 생성자 |
boolean | Boolean | Boolean(boolean value) Boolean(String s) |
char | Character | Character(char value) |
byte | Byte | Byte(byte value) Byte(String s) |
short | Short | Short(short value) Short(String s) |
int | Integer | Integer(int value) Integer(String s) |
long | Long | Long(long value) Long(String s) |
float | Float | Float(double value) Float(float value) Float(String s) |
double | Double | Double(double value) Double(String s) |
Number클래스
이 클래스는 추상클래스로 내부적으로 숫자를 멤버변수로 갖는 래퍼클래스들의 조상이다. Number의 자손으로 Byte, Short, Integer, Long, Float, Double, BigInteger, BigDecimal이 있다.
문자열을 숫자로 변환하기
문자열을 숫자로 변환하는 방법은 대표적으로 세가지가 있다.
int i = new Integer("100").intValue();
int i2 = Integer.parseInt("100");
int i3 = Integer.valueOf("100");
parseInt()와 valueOf()는 동작은 같지만 전자는 반환값이 기본형이고 후자는 반환값이 래퍼클래스 타입이다. 성능은 valueOf()가 조금 더 느리다.
문자열이 10진수가 아닌 다른 진법의 숫자일 때도 변환이 가능하도록 다음과 같은 메서드가 제공된다.
// 문자열 s를 radix진법으로 인식
static int parseInt(String s, int radix)
static Integer valueof(String s, int radix)
오토박싱 & 언박싱(autoboxing & unboxing, jdk 1.5~)
JDK1.5 이전에는 기본형과 참조형 간의 연산이 불가능했기 때문에 래퍼클래스로 기본형을 객체로 만들어서 연산해야 했다. 그러나 이제는 기본형과 참조형 간의 덧셈이 가능하다. 자바 언어의 규칙이 바뀐 것은 아t니고, 컴파일러가 자동으로 변환하는 코드(intValue() 같은)를 넣어주기 때문이다.
이 외에도 내부적으로 객체 배열을 가지고 있는 Vector클래스나 ArrayList클래스에 기본형 값을 저장해야 할 때나 형변환이 필요할 때도 컴파일러가 자동적으로 코드를 추가해준다. 기본형 값을 래퍼클래스의 객체로 변환해주는 것을 오토박싱이라고 하고, 반대로 래퍼클래스의 객체를 기본형으로 변환하는 것을 언박싱이라고 한다.
2. 유용한 클래스
2.1 java.utilObjects 클래스
Object클래스의 보조 클래스로 모든 메서드가 static이다. 객체의 비교나 널 체크에 유용하다.
메서드 | 설명 |
isNull() | 해당 객체가 널인지 확인해서 null이면 true를, 아니면 false를 반환한다. |
nonNull() | isNull()과 반대된다. |
requireNonNull() | 해당 객체가 널이 아니어야 하는 경우에 사용한다. 만일 객체가 널이면 NullPointerException을 발생시킨다. |
compare(Object a, Object b, Comparator c) | 두 비교대상 a와 b가 같으면 0, 크면 양수, 작으면 음수를 반환한다. 두 객체를 비교하는 기준은 c에 따라 정해진다. |
equals(Object a, Object b) | Object의 equals()와 같이 a와 b를 비교한다. null 검사를 추가로 하지 않아도 된다. a와 b가 모두 null일 경우 true를 반환한다. |
deepEquals() | 객체를 재귀적으로 비교해 다차원 배열의 비교도 가능하다. |
toString() | Object의 toString()과 같은 행동을 하지만 내부적으로 null 검사를 진행하도록 정의되어 있다. |
hashCode() | 내부적으로 null을 검사하고 Object 캘르스의 hashCode()를 호출한다. null일 때에는 0을 반환한다. |
2.2 java.util.Random클래스
난수를 얻는 방법으로는 Math.random() 외에도 Random클래스를 이용하는 방법이 있다. 사실 Math.random()은 내부적으로 Random클래스의 인스턴스를 생성해서 사용하는 것이므로 둘 중에서 편한 것을 사용하면 된다. 예제별 두 문장은 같은 의미를 가진다.
// 난수 하나 저장
double randNum = Math.random();
double randNum = new Random().nextDouble();
// 1~6사이의 수 중 랜덤으로 하나 저장
int num = (int) (Math.random() * 6) + 1;
int num = new Random.nextInt(6) + 1;
Math의 random()메서드와 Random의 가장 큰 차이점이라면 종자값(seed)을 설정할 수 있다는 것이다. 종자값이 같은 Random인스턴스들은 항상 같은 낭수를 같은 순서대로 반환한다. 종자값은 난수를 만든느 공식에 사용되는 값으로 같은 공식에 같은 값을 넣으면 같은 겨로가를 얻는 것처럼 같은 종자값을 넣으면 같은 난수를 얻게 된다.
Random클래스의 생성자와 메서드
메서드 / 생성자 | 설명 |
Random() | 현재시간(System.currentTimeMillis())을 종자값(seed)으로 이용하는 Random인스턴스를 생성한다. |
Random(long seed) | 매개변수 seed를 종자값으로 하는 Random인스턴스를 생성한다. |
boolean nextBoolean() | boolean타입의 난수를 반환한다. |
void nextBytes(byte[] bytes) | bytes배열에 byte타입의 난수를 채워서 반환한다. |
double nextDouble() | double타입의 난수를 반환한다. (0.0 <= x < 1.0) |
float nextFloat() | float타입의 난수를 반환한다. (0.0 <= x < 1.0) |
double nexftGaussian() | 평균은 0.0이고 표준편차는 1.0인 가우시안(Gaussian)분포에 따른 double형의 난수를 반환한다. |
int nextInt() | int타입의 난수를 반환한다. (0.0 <= x < 1.0) |
int nextInt(int n) | 0~n의 범위에 있는 int값을 반환한다.(0 <= x < n) |
long nextLong() | long타입의 난수를 반환한다. (long범위) |
void setSeed(long seed) | 종자값을 주어진 값(seed)으로 변경한다. |
2.3 정규식(Regular Expression) - java.util.regex패키지
정규식: 텍스트 데이터 중에서 원하는 조건(패턴)과 일치하는 문자열을 찾아 내기 위해 사용하는 것으로 미리 정의된 기호와 문자를 이용해서 작성한 문자열.
정규식을 이용하면 많은 양의 텍스트 파일 중에서 원하는 데이터를 손쉽게 뽑아낼 수 있고 입력된 데이터가 형식에 맞는지 체크할 수도 있다.
정규식 사용하기
1. 정규식을 매개변수로 Pattern클래스의 static메서드인 Patter compile(String regex)을 호출하여 Pattern인스턴스를 얻는다.
Pattern p = Pattern.compile(" /* 정규식 */ ");
2. 정규식으로 비교할 대상을 매개변수로 Pattern클래스의 Matcher matcher(CharSequence input)를 호출해서 Matcher인스턴스를 얻는다.Matcher m = p.matcher(data[i]);
3. Matcher인스턴스에 boolean matches()를 호출해서 정규식에 부합하는지 확인한다.
if(m.matches())
다양한 정규식 패턴
정규식 패턴 | 설명 |
c[a-z]* | c로 시작하는 영단어 |
c[a-z] | c로 시작하는 두 자리 영단어 |
c[a-zA-z] | c로 시작하는 두 자리 영단어(a~z 또는 A~Z, 즉 대소문자 구분안함) |
c[a-zA-Z0-9] c\w |
c로 시작하고 숫자와 영어로 조합된 두 글자 |
.* | 모든 문자열 |
c. | c로 시작하는 두 자리 문자열 |
c.* | c로 시작하는 모든 문자열(기호포함) |
c\. | c.와 일치하는 문자열. '.'은 패턴 작성에 사용되는 문자이므로 escape문자인 '\'를 사용해야한다. |
c\d c[0-9] |
c와 숫자로 구성된 두 자리 문자열 |
c.*t | c로 시작하고 t로 끝나는 모든 문자열 |
[b|c].* [bc].* [b-c].* |
b 또는 c로 시작하는 문자열 |
[^b|c].* [^bc].* [^b-c].* |
b 또는 c로 시작하지 않는 문자열 |
.*a.* | a를 포함한느 모든 문자열 *: 0 또는 그 이상의 문자 |
.*a.+ | a를 포함하는 모든 문자열 +: 1 또는 그 이상의 문자. '+'는 '*'과 달리 반드시 하나 이상의 문자가 있어야 하므로 a로 끝나는 단어는 포함되지 않았다. |
[b|c].{2} | b 또는 c로 시작하는 세 자리 문자열.(b 또는 c 다음에 두 자리이므로 모두 세 자리) |
그룹화
정규식의 일부를 괄호로 묶어서 그룹화할 수 있다. 그룹화된 부분은 하나의 단위로 묶이는 셈이 되어서 한 번 또는 그 이상을 의미하는 '+'나 '*'가 뒤에 오면 그룹화된 부분이 적용대상이 된다. 그리고 그룹화된 부분은 group(int i)를 이용해 나누어 얻을 수 있다.
class RegularEx3 {
public static void main(String[] args) {
String source = "HP: 010-1111-1111, HOME: 02-999-9999";
String pattern = "(0\\d{1, 2}) - (\\d{3, 4}) - (\\d{4})";
Pattern p = Pattern.compile(patter);
Matcher m = p.matcher(source);
int i = 0;
while(m.find()) {
System.out.println(++i + ": " + m.group() + " -> " + m.group(1)
+ ", " + m.group(2) + ", " + m.group(3));
}
}
}
/* 결과
* 1: 010-1111-1111 -> 010, 1111, 1111
* 2: 02-999-9999 -> 02, 999, 9999
*/
pattern은 괄호를 이용해 정규식을 세 부분으로 나누었는데 예제와 결과에서 알 수 있듯이 매칭되는 문자열에서 첫 번째 그룹은 group(1)로 두 번째 그룹은 group(2)와 같이 호출함으로써 얻을 수 있다. group()(= group(0))은 그룹으로 매칭된 문자열을 나누지 않고 그대로 반환한다. find()는 주어진 소스 내에서 패턴과 일치하는 부분을 찾아내면 true를 반환하고 찾지 못하면 false를 반환한다.
<이하 생략>
2.4 java.util.Scanner클래스
Scanner는 화면, 파일, 문자열과 같은 입력소스로부터 문자데이터를 읽어오는데 도움을 줄 목적으로 jdk1.5부터 추가되었다. Scanner는 정규식 표현을 이용한 라인단위의 검색을 지원하며 구분자에도 정규식 표현을 사용할 수 있어서 복잡한 형태의 구분자도 처리가 가능하다.
<중략>
2.5 java.util.StringTokenizer클래스
StringTokenizer는 긴 문자열을 지정된 구분자를 기준으로 토큰이라는 여러 개의 문자열로 잘라내는 데 사용된다. StringTokenizer를 이용하는 방법 외에도 아래와 같이 String의 split(String regex)이나 Scanner의 useDelimiter(String pattern)를 사용할 수 있지만 이 두가지 방법은 정규식 표현을 사용해야하므로 정규식 표현에 익숙하지 않은 경우 StringTokenizer를 사용하는 것이 간단하면서도 명확한 결과를 얻을 수 있다. 그러나 StringTokenizer는 구분자로 단 하나의 문자 밖에 사용하지 못하기 때문에 보다 복잡한 형태의 구분자로 문자열을 나누어야 할 때는 어쩔 수 없이 정규식을 사용하는 메서드를 사용해야 할 때는 어쩔 수 없이 정규식을 사용하는 메서드를 사용해야 할 것이다.
StringTokenizer의 생성자와 메서드
메서드 / 생성자 | 설명 |
StringTokenizer(String str, String delim) | 문자열(str)을 지정된 구분자(delim)로 나누는 StringTokenizer를 생성한다.(구분자는 토큰으로 간주되지 않음) |
StringTokenizer(String str, String delim, boolean returnDelims) | 문자열(str)을 지정된 구분자(delim)로 나누는 STringTokenizer를 생성한다. returnDelims의 값을 true로 하면 구분자도 토큰으로 간주된다. |
int countTokens() | 전체 토큰의 수를 반환한다. |
boolean hasMoreTokens() | 토큰이 남아있는지 알려준다. |
String nextToken() | 다음 토큰을 반환한다. |
ㄹ
split() vs StringTokenizer()
split()은 빈 문자열도 토큰으로 인식하는 반면 StringTokenizer는 빈 문자열을 토큰으로 인식하지 않기 때문에 같은 문자열을 쪼개도 인식하는 토큰의 개수가 서로 다르다. 이 외에도 성능의 차이가 있는데 split()은 데이터를 토큰으로 잘라낸 결과를 배열에 담아서 반환하는데 StringTokenizer는 데이터를 토큰으로 바로바로 잘라서 반환하므로 split()이 성능이 더 떨어질 수밖에 없다.
2.6 java.math.BigInteger클래스
정수형으로 표현할 수 있는 값의 한계가 있다. 가장 큰 정수형 타입인 long의 범위도 상당히 크지만 과학적 계선에서는 더 큰 값을 다뤄야할 때가 있다. 그럴 때 사용하면 좋은 것이 BigInteger클래스이다. BigInteger는 내부적으로 int배열을 사용해서 값을 다룬다. 그래서 long타입보다 훨씬 큰 값을 다룰 수 있는 것이다. 대신 성능은 long타입보다 떨어질 수밖에 없다.
BigInteger는 변경이 불가능한 클래스이다. 그리고 모든 정수형이 그렇듯이 BigInteger 역시 값을 '2의 보수'형태로 표현한다. 또한 부호를 다로 저장하고 배열에는 값 자체만 저장한다. 그래서 부호값이 -1, 즉 음수인 경우, 2의 보수법에 맞게 값을 변환해서 처리한다.
BigInteger의 생성
BigInteger를 생성하는 방법은 대표적으로 세 가지가 있다.
BigInteger val1 = new BigInteger("384791749234873412341098"); // 문자열로 생성(best! 값의 한계때문.)
BigInteger val2 = new BigInteger("FFFF", 16); // n진수(radix)의 문자열로 생성
BigInteger val3 = BigInteger.valueOf(124018351L); // 숫자로 생성.
다른 타입으로의 변환
BigInteger도 Number클래스로부터 상속받은 기본형으로 변환하는 메서드들과 변환한 결과가 변환한 타입의 범위에 속하지 않으면 ArithmeticException을 발생시키는 Exact()메서드들도 가지고 있다.
// 기본형으로 변환하는 메서드
int intValue()
long longValue()
float floatValue()
double doubleValue()
// 변환한 결과가 변환한 타입의 범위에 속하지 않으면 ArithmeticException을 발생시키는 메서드
byte byteValueExact()
int intValueExact()
long longValueExact()
BigInteger의 연산
BigInteger에는 정수형에 사용할 수 있는 수학적인 계산을 쉽게 해주는 메서드들이 정의되어 있다. 기본적인 연산을 수행하는 메서드 몇 개만 고르면 아래와 같다.
// 정수 연산 메서드
BigInteger add(BigInteger val) // this + val
BigInteger subtract(BigInteger val) // this - val
BigInteger multiple(BigInteger val) // this * val
BigInteger divide(BigInteger val) // this / val
BigInteger remainder(BigInteger val) // this % val
BigInteger는 불변이므로 반환타입이 BigInteger란 얘기는 새로운 인스턴스가 반환된다는 뜻이다.(다른 불변클래스도 마찬가지)
비트 연산 메서드
<중략>
2.7 java.math.BigDecimal클래스
double타입의 범위도 상당히 넓지만 정밀도가 13자리 밖에 되지 않고 실수형의 특성상 오차를 피할 수 없다. BigDecimal은 실수형과 달리 정수를 이용해서 실수를 표현한다. 실수의 오차는 10진 실수를 2진 실수로 정확히 변환할 수 없는 경우가 있기 때문에 발생하는 것이므로, 오차가 없는 2진 정수로 변환하여 다루는 것이다. 실수를 정수와 10의 제곱의 곱으로 표현한다.
BigDecimal의 생성
BigDecimal을 생성하는 방법은 여러 가지가 있다.
BigDecimal val1 = new BigDecimal("123.4567890"); // 문자열로 생성(best! 값의 한계 때문)
BigDecimal val2 = new BigDecimal(123.4567890); // double타입의 리터럴로 생성
BigDecimal val3 = new BigDecimal(123456); // int, long타입의 리터럴로 생성
BigDecimal val4 = BigDecimal.valueOf(123.456); // 생성자 대신 valueOf(double) 사용
BigDecimal val5 = BigDecimal.valueOf(123456); // 생성자 대신 valueOf(int) 사용
double타입의 값을 매개변수로 갖는 생성자를 사용하면 오차가 발생할 수 있다.
다른 타입으로의 변환 && BigDecimal의 연산
<생략: BigInteger 내용과 유사>
반올림 모드 - divide()와 setScale()
다른 연산과 달리 나눗셈을 처리하기 위한 메서드는 다음과 같이 다양한 버젼이 존재한다. 나눗셈의 결과를 어떻게 반올림 처리할지, 몇 번째 자리에서 반올림할 것인지를 지정할 수 있다. BigDecimal이 아무리 오차없이 실수를 저장한다 해도 나눗셈에서 발생하는 오차는 어쩔 수 없다.
BigDecimal divide(Bigdecimal divisor)
BigDecimal divide(Bigdecimal divisor, int roundingMode)
BigDecimal divide(Bigdecimal divisor, RoundingMode roundingMode)
BigDecimal divide(Bigdecimal divisor, int scale, int roundingMode)
BigDecimal divide(Bigdecimal divisor, int scale, RoundingMode roundingMode)
BigDecimal divide(Bigdecimal divisor, MaxContext mc)
roundingMode는 반올림 처리방법에 대한 것으로 BigDecimal에 정의된 ROUND_로 시작하는 상수들 중 하나를 선택해서 사용하면 된다. RoundingMode는 이 상수들을 열겨형으로 정의한 것으로 나중에 추가되었다. 가능하면 열거형 RoundingMode를 사용하자
열거형 RoundingMode의 상수 | 설명 |
CEILING | 올림 |
FLOOR | 내림 |
UP | 양수일 때는 올림, 음수일 때는 내림 |
DOWN | 양수일 때는 내림, 음수일 때는 올림 |
HALF_UP | 반올림(5이상 올림, 5미만 버림) |
HALF_EVEN | 반올림(반올림 자리의 값이 짝수면 HALF_DOWN, 홀수면 HALF_UP) |
HALF_DOWN | 반올림(6이상 올림, 6미만 버림) |
UNNECESSARY | 나눗셈의 결과가 딱 떨어지는 수가 아니면, ArithmeticException 발생 |
divide()로 나눗셈한 결과가 무한소수인 경우, 반올림 모드를 지정해주지 않으면 ArithmeticException이 발생한다는 것이다.
java.math.MathContext && scale 변경
<하략>