1. java.lang 패키지
java.lang패키지는 자바 프로그래밍에 가장 기본이 되는 클래스들을 포함하고 있다. 그렇기 때문에 java.lang패키지의 클래스들은 import문 없이도 사용할 수 있게 되어 있다.
자주 사용되는 클래스 몇 가지만 학습해보자.
1-1. Object 클래스
Object 클래스는 모든 클래스의 최상위클래스이기 때문에 Object 클래스의 멤버들은 모든 클래스에서 바로 사용이 가능하다.
- equals(Object obj)
매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean값으로 반환한다.
class EqualsEx {
public static void main(String[] args) {
Value v1 = new Value(10);
Value v2 = new Value(10);
if (v1.equals(v2))
System.out.println("v1과 v2는 같습니다.");
else
System.out.println("v1과 v2는 다릅니다.");
v2 = v1;
if (v1.equals(v2))
System.out.println("v1과 v2는 같습니다.");
else
System.out.println("v1과 v2는 다릅니다.");
}
}
class Value {
int value;
Value(int value) {
this.value = value;
}
}
//결과
v1과 v2는 다릅니다.
v1과 v2는 같습니다.
equals 메서드는 주소값으로 비교를 하기 때문에 두 Value 인스턴스의 멤버변수 value의 값이 10으로 서로 같을지라도 비교한 결과는 false일 수 밖에 없다.
하지만 v2 = v1; 을 수행한 후에는 참조변수 v2에 v1이 참조하고 있는 인스턴스의 주소값이 저장되므로 결과는 true가 된다.
만약 주소값이 아니라 실제 저장된 값을 비교하도록 하려면 Value 클래스에서 equals 메서드를 오버라이딩하여 객체에 저장된 내용을 비교하도록 변경하면 된다. 다음 예제를 보자.
class Person {
long id;
public boolean equals(Object obj) {
if(obj instanceof Person)
return id == ((Person) obj).id;
else
return false;
}
Person(long id) {
this.id = id;
}
}
class EqualsEx {
public static void main(String[] args) {
Person p1 = new Person(8011081111222L);
Person p2 = new Person(8011081111222L);
if (p1==p2)
System.out.println("p1과 p2는 같은 사람입니다.");
else
System.out.println("p1과 p2는 다른 사람입니다.");
if (p1.equals(p2))
System.out.println("p1과 p2는 같은 사람입니다.");
else
System.out.println("p1과 p2는 다른 사람입니다.");
//결과
p1과 p2는 다른 사람입니다.
p1과 p2는 같은 사람입니다.
equals 메서드가 Person 인스턴스의 주소값이 아닌 멤버변수 id의 값을 비교하도록 하기 위해 equals 메서드를 오버라이딩했다.
이렇게 함으로써 서로 다른 인스턴스일지라도 같은 id를 가지고 있다면 true를 결과로 얻게 할 수 있다.
- hashCode()
해싱 기법에 사용되는 ‘해시함수(hash function)’를 구현한 것이다.
해싱은 데이터관리기법 중의 하나인데 다량의 데이터를 저장하고 검색하는 데 유용하다.
해시함수는 찾고자하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드(hash code)를 반환한다.
💡 일반적으로 해시코드가 같은 두 객체가 존재하는 것이 가능하지만 Object 클래스에 정의된 hashCode 메서드는 객체의 주소값으로 해시코드를 만들어 반환하기 때문에 32 bit JVM에서는 서로 다른 두 객체는 결코 같은 해시코드를 가질 수 없었지만, 64 bit JVM에서는 8 byte 주소값으로 해시코드(4 byte)를 만들기 때문에 해시코드가 중복될 수 있다.
String 클래스는 문자열의 내용이 같으면 동일한 해시코드를 반환하도록 hashCode메서드가 오버라이딩되어 있다.
반면 System.identityHashCode(Object x)는 Object 클래스의 hashCode메서드처럼 객체의 주소값으로 해시코드를 생성하기 때문에 모든 객체에 대해 항상 다른 해시코드 값을 반환할 것을 보장한다.
class HashCodeEx {
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1.equals(str2));
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
System.out.println(System.identityHashCode(str1));
System.out.println(System.identityHashCode(str2));
}
}
//결과
true
96354
96354
27134973
1284693
- toString()
인스턴스 변수에 저장된 값들을 문자로 표현한다.
Object 클래스에 정의된 toString()은 아래와 같다.
public String toString() {
return getClass().getName()+"@"+Integer.toHexString(hashCode());
}
클래스를 작성할 때 toString()을 오버라이딩하지 않는다면, 위와 같은 내용이 그대로 사용될 것이다.
즉, toString()을 호출하면 클래스 이름에 16진수의 해시코드를 얻게 될 것이다.
class Card {
String kind;
int number;
Card() {
this("SPADE", 1);
}
Card(String kind, int number) {
this.kind = kind;
this.number = number;
}
}
class CardToString {
public static void main(String[] args) {
Card c1 = new Card();
Card c2 = new Card();
System.out.println(c1.toString());
System.out.println(c2.toString());
}
}
//결과
Card@19e0bfd
Card@139a55
만약 원하는 결과를 출력하고 싶다면 Card 클래스에서 toString()을 오버라이딩하면 된다.
class Card {
String kind;
int number;
Card() {
this("SPADE", 1);
}
Card(String kind, int number) {
this.kind = kind;
this.number = number;
}
**public String toString() {
return "kind : " + kind + ", number : " + number;
}**
}
class CardToString {
public static void main(String[] args) {
Card c1 = new Card();
Card c2 = new Card("HEART", 10);
System.out.println(c1.toString());
System.out.println(c2.toString());
}
}
//결과
kind : SPADE, number : 1
kind : HEART, number : 10
- clone()
이 메서드는 자신을 복제하여 새로운 인스턴스를 생성하는 일을 한다.
Object 클래스에 정의된 clone()은 단순히 인스턴스 변수의 값만 복사하기 때문에 참조타입의 인스턴스 변수가 있는 클래스는 완전한 인스턴스 복제가 이루어지지 않는다.
예를 들어 배열의 경우, 복제된 인스턴스도 같은 배열의 주소를 갖기 때문에 복제된 인스턴스의 작업이 원래의 인스턴스에 영향을 미치게 된다.
clone()을 사용하려면 먼저 복제할 클래스가 Cloneable 인터페이스를 구현해야 하고, clone()을 오버라이딩 하면서 접근 제어자를 protected에서 public으로 변경한다.
그래야만 상속관계가 없는 다른 클래스에서 clone()을 호출할 수 있다.
public class Object {
...
protected native Object clone() throws CloneNotSupportedException;
...
}
class Point implements Cloneable {
...
public Object clone() {
Object obj=null;
try {
obj = super.clone();
} catch(CloneNotSupportedException e) {}
return obj;
}
}
Cloneable 인터페이스를 구현한 클래스의 인스턴스만 clone()을 통한 복제가 가능한 이유는 인스턴스의 데이터를 보호하기 위해서이다.
Cloneable 인터페이스가 구현되어 있다는 것은 클래스 작성자가 복제를 허용한다는 의미이다.
- 공변 반환타입
JDK1.5부터 ‘공변 반환타입(covariant return type)’이라는 것이 추가되었는데, 이 기능은 오버라이딩할 때 조상 메서드의 반환타입을 자손 클래스의 타입으로 변경을 허용하는 것이다.
공변 반환타입을 사용하면, 조상의 타입이 아닌 실제로 반환되는 자손 객체의 타입으로 반환할 수 있어서 번거로운 형변환이 줄어든다는 장점이 있다.
Point copy = (Point)original.clone(); → Point copy = original.clone();
class CloneEx {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
int[] arrClone = arr.clone();
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]
일반적으로 배열을 복사할 때 같은 길이의 배열을 생성한 후 System.arraycopy()를 이용해서 내용을 복사하지만, 이처럼 clone()을 이용해서 간단하게 복사할 수 있다는 것도 참고로 알아두자.
배열 뿐 아니라 Vector, ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, Calendar, Date와 같은 클래스들이 이와 같은 방식으로 복제가 가능하다.
- 얕은 복사와 깊은 복사
clone()은 단순히 객체에 저장된 값을 그대로 복제할 뿐, 객체가 참조하고 있는 객체까지 복제하지는 않는다.
객체배열을 clone()으로 복제하는 경우에는 원본과 복제본이 같은 객체를 공유하므로 완전한 복제라고 보기 어렵다. 이러한 복제를 ‘얕은 복사(shallow copy)’라고 한다. 얕은 복사에서는 원본을 변경하면 복사본도 영향을 받는다.
반면 원본이 참조하고 있는 객체까지 복제하는 것을 ‘깊은 복사(deep copy)’라고 하며, 깊은 복사에서는 원본과 복사본이 서로 다른 객체를 참조하기 때문에 원본의 변경이 복사본에 영향을 미치지 않는다.
💡 일반적으로 객체 배열의 깊은 복사는 반복문을 사용해 new 키워드로 새로운 객체를 만들고, 값을 넣어줌으로 구현한다.
- getClass()
자신이 속한 클래스의 Class 객체를 반환하는 메서드다. Class객체는 이름이 ‘Class”인 클래스의 객체다.
Class 객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재한다. 그리고 클래스 파일이 ‘클래스 로더’에 의해서 메모리에 올라갈 때 자동으로 생성된다.
먼저 기존에 생성된 클래스 객체가 메모리에 존재하는지 확인하고, 있으면 객체의 참조를 반환하고 없으면 클래스 패스에 지정된 경로를 따라서 클래스 파일을 찾는다. 못 찾으면 ClassNotFoundException이 발생하고, 찾으면 해당 클래스 파일을 읽어서 Class 객체로 변환한다.
- Class객체를 얻는 방법
클래스의 정보가 필요할 때, 먼저 Class 객체에 대한 참조를 얻어 와야 하는데 해당 Class 객체에 대한 참조를 얻는 방법은 여러 가지가 있다.
Class CObj = new Card().getClass(); // 생성된 객체로부터 얻는 방법
Class cObj = Card.class; // 클래스 리터럴(*.class)로부터 얻는 방법
Class cObj = Class.forName("Card"); // 클래스 이름으로부터 얻는 방법
이 중 forName()은 특정 클래스 파일, 예를 들어 데이터베이스 드라이버를 메모리에 올릴 때 주로 사용한다.
Class 객체를 이용하면 클래스에 정의된 멤버의 이름이나 개수 등, 클래스에 대한 모든 정보를 얻을 수 있기 때문에 Class 객체를 통해서 객체를 생성하고 메서드를 호출하는 등 보다 동적인 코드를 작성할 수 있다.
Card c = new Card(); // new연산자를 이용한 객체 생성
Card c = Card.class.newInstance(); // Class객체를 이용한 객체 생성
💡 new 연산자를 사용하면 쉽게 객체를 생성할 수 있는데 굳이 Class 객체를 이용해야 할까?
→ 만약 클래스의 종류가 실행할 때 결정이 된다면 new 연산자로 객체 생성이 불가능하다!
Class 객체를 이용하면 동적으로 객체를 생성할 수 있다.
1-2. String 클래스
자바에서는 문자열을 위한 클래스를 제공한다. 그것이 바로 String 클래스인데 문자열을 저장하고 이를 다루는데 필요한 메서드를 함께 제공한다.
- 변경 불가능한(immutable) 클래스
String 클래스에는 문자열을 저장하기 위해서 문자형 배열 참조변수(char[]) value를 인스턴스 변수로 정의해놓고 있다.
한번 생성된 String 인스턴스가 갖고 있는 문자열은 읽어 올 수만 있고, 변경할 수는 없다.
예를 들어 ‘+’연산자를 이용해서 문자열을 결합하는 경우 인스턴스 내의 문자열이 바뀌는 것이 아니라 새로운 문자열이 담긴 String 인스턴스가 생성되는 것이다.
따라서 문자열간의 결합이나 추출 등 문자열을 다루는 작업이 많이 필요한 경우에는 StringBuffer클래스를 사용하는 것이 좋다.
- 문자열의 비교
문자열을 만들 때는 문자열 리터럴을 지정하는 방법과 String 클래스의 생성자를 사용해서 만드는 방법이 있다.
String str1 = "abc";
String str2 = "abc";
String str3 = new String("abc");
String str4 = new String("abc");
String 클래스의 생성자를 이용한 경우에는 new 연산자에 의해서 메모리할당이 이루어지기 때문에 항상 새로운 String 인스턴스가 생성된다.
그러나 문자열 리터럴은 이미 존재하는 것을 재사용하는 것이다.
public class Main {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
if(str1==str2) {
System.out.println("str1 == str2");
} else {
System.out.println("str1 != str2");
}
if(str1.equals(str2)) {
System.out.println("str1.equals(str2) = true");
} else {
System.out.println("str1.equals(str2) = false");
}
String str3 = new String("abc");
String str4 = new String("abc");
if(str3==str4) {
System.out.println("str3 == str4");
} else {
System.out.println("str3 != str4");
}
if(str3.equals(str4)) {
System.out.println("str3.equals(str4) = true");
} else {
System.out.println("str3.equals(str4) = false");
}
}
}
//결과
str1 == str2
str1.equals(str2) = true
str3 != str4
str3.equals(str4) = true
- 문자열 리터럴
자바 소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다.
같은 내용의 문자열 리터럴은 한 번만 저장된다. 문자열 리터럴도 String 인스턴스이고 한 번 생성하면 내용을 변경할 수 없으니 하나의 인스턴스를 공유하면 되기 때문이다.
- join()과 StringJoiner
join()과 StringJoiner는 JDK1.8부터 추가되었다.
join()은 여러 문자열 사이에 구분자를 넣어서 결합한다. 구분자로 문자열을 자르는 split()과 반대의 작업을 한다고 생각하면 이해하기 쉽다.
String animals = "dog,cat,bear";
String[] arr = animals.split(",");
String str = String.join("-", arr);
System.out.println(str);
//결과
dog-cat-bear
java.util.StringJoiner 클래스를 사용해서 문자열을 결합할 수도 있다.
StringJoiner sj = new StringJoiner(",", "[", "]");
String[] strArr = {"aaa", "bbb", "ccc"};
for(String s : strArr)
sj.add(s.toUpperCase());
System.out.println(sj.toString());
//결과
[AAA,BBB,CCC]
- 문자 인코딩 변환
getBytes(String charsetName)를 사용하면, 문자열의 문자 인코딩을 다른 인코딩으로 변경할 수 있다.
자바가 UTF-16을 사용하지만, 문자열 리터럴에 포함되는 문자들은 OS의 인코딩을 사용한다.
서로 다른 문자 인코딩을 사용하는 컴퓨터 간에 데이터를 주고받을 때에는 적절한 문자 인코딩이 필요하다.
byte[] bArr = str.getBytes("UTF-8");
- 기본형과 문자열 간의 변환
먼저 기본형 값을 String으로 변환할 때는 숫자에 빈 문자열(””)을 더해주거나 valueOf()를 사용하면 된다.
성능은 valueOf()가 더 좋지만 빈 문자열을 더하는 방법이 간단하고 편하다.
반대로 String을 기본형 값으로 변환하는 방법은 valueOf()를 사용하거나 parseInt()를 사용하면 된다.
원래 valueOf()의 반환타입은 Integer인데 오토박싱(auto-boxing)에 의해 Integer가 int로 자동 변환된다.
int i = 10;
String s = String.valueOf(i);
int k = Integer.valueOf(s)
//혹은 int k = Integer.parseInt(s);
💡 valueOf()는 Integer 래퍼 객체를 반환하고 parseInt()는 기본 int값이 반환된다.
또한 두 메서드 모두 문자열에 공백이 있을 경우 변환 시 예외가 발생할 수 있어서 trim()을 같이 사용하는 경우가 많다.
int val = Integer.parseInt(” 123 “.trim());
1-3. StringBuffer클래스와 StringBuilder클래스
String 클래스는 인스턴스를 생성할 때 지정된 문자열을 변경할 수 없지만 StringBuffer클래스는 변경이 가능하다. 내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있으며, StringBuffer인스턴스를 생성할 때 그 크기를 지정할 수 있다.
- StringBuffer의 생성자
StringBuffer인스턴스를 생성할 때는 생성자 StringBuffer(int length)를 사용해서 충분히 여유있는 크기로 지정하는 것이 좋다. 버퍼의 크기를 지정하지 않으면 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성한다.
버퍼의 크기가 작업하려는 문자열의 길이보다 작을 때는 내부적으로 버퍼의 크기를 증가시키는 작업이 수행되는데, 새로운 길이의 배열을 생성한 후에 이전 배열의 값을 복사하는 것이다.
- StringBuffer의 변경
StringBuffer sb = new StringBuffer("abc");
sb.append("123");
System.out.println(sb)
//결과
abc123
StringBuffer sb2 = sb.append("zz");
System.out.println(sb)
System.out.println(sb2)
//결과
abc123zz
abc123zz
append()는 반환타입이 StringBuffer인데 자신의 주소를 반환한다. 즉, 새로운 문자열이 추가되고 자신의 주소를 반환하는 것이다.
- StringBuffer의 비교
StringBuffer클래스는 equals메서드를 오버라이딩하지 않아서 StringBuffer클래스의 equals메서드를 사용해도 등가비교연산자(==)로 비교한 것과 같은 결과를 얻는다.
만약 문자열로 비교하기 위해서는 toString()을 호출해서 String인스턴스를 얻은 다음 여기에 equals메서드를 사용해서 비교해야 한다.
StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
System.out.println(sb==sb2); //false
System.out.println(sb.equals(sb2); //false
String s = sb.toString();
String s2 = sb2.toString();
System.out.println(s.equals(s2)); //true
- StringBuilder란?
StringBuffer는 멀티쓰레드에 안전(thread safe)하도록 동기화되어 있다.
멀티쓰레드로 작성된 프로그램이 아닌 경우 StringBuffer의 동기화는 불필요하게 성능만 떨어뜨리게 된다.
그래서 StringBuffer에서 쓰레드의 동기화만 뺀 StringBuilder가 새로 추가되었다.
1-5. 래퍼(wrapper) 클래스
객체지향 개념에서 모든 것은 객체로 다루어져야 한다. 그러나 자바에서는 8개의 기본형을 객체로 다루지 않는데 이것이 바로 자바가 완전한 객체지향 언어가 아니라는 얘기를 듣는 이유이다. 그 대신 보다 높은 성능을 얻을 수 있었다.
때로는 기본형 변수도 어쩔 수 없이 객체로 다뤄야 하는 경우가 있는데 이 때 사용되는 것이 래퍼(wrapper)클래스이다.
래퍼 클래스의 생성자는 매개변수로 문자열이나 각 자료형의 값들을 인자로 받는다. 이 때 주의해야 할 것은 생성자의 매개변수로 문자열을 제공할 때, 각 자료형에 알맞는 문자열을 사용해야 한다는 것이다.
예를 들어 new Integer(”1.0”);과 같이 하면 NumberFormatException이 발생한다.
- 오토박싱 & 언박싱(autoboxing & unboxing)
JDK1.5 이전에는 기본형과 참조형 간의 연산이 불가능했지만 이제는 컴파일러가 자동으로 변환하는 코드를 넣어줘서 기본형과 참조형의 연산이 가능하다.
이렇게 기본형 값을 래퍼 클래스의 객체로 자동 변환해주는 것을 ‘오토박싱(autoboxing)’이라고 하고, 반대로 변환하는 것을 ‘언박싱(unboxing)’이라고 한다.
2. 유용한 클래스
2-1. java.util.Objects클래스
Object클래스의 보조 클래스로 객체의 비교나 널 체크(null check)에 유용하다.
static boolean isNull(Object obj)
static boolean nonNull(Object obj)
requireNonNull()은 해당 객체가 널이 아니어야 하는 경우에 사용한다.
기존의 매개변수 유효성 검사
void setName(String name) {
if(name==null)
throw new NullPointerException("name must not be null.");
this.name = name;
}
requireNonNull()을 사용한 매개변수 유효성 검사
void setName(String name) {
this.name = Objects.requireNonNull(name, "name must not be null.");
}
Object클래스에 정의된 equals()와 Objects클래스에 정의된 equals()의 차이점은 null검사 여부에 있다.
if(a!=null && a.equals(b)) { //a가 null인지 반드시 확인해야 한다.
...
}
if(Objects.equals(a, b)) { //매개변수의 값이 null인지 확인할 필요가 없다.
...
}
Objects클래스의 equals()는 내부에서 널 검사를 하기 때문에 따로 넣지 않아도 되는 것이다.
또 하나의 유용한 기능으로 deepEquals()가 있다.
이 메서드는 객체를 재귀적으로 비교하기 때문에 다차원 배열의 비교도 가능하다.
String[][] str2D = new String[][]{{"aaa","bbb"},{"AAA","BBB"}};
String[][] str2D2 = new String[][]{{"aaa","bbb"},{"AAA","BBB"}};
System.out.println(Objects.equals(str2D, str2D2)); //false
System.out.println(Objects.deepEquals(str2D, str2D2)); //true
2-2. java.util.Random클래스
난수를 얻는 방법으로 Math.random()과 동일한 기능을 한다.
Math.random()은 내부적으로 Random클래스의 인스턴스를 생성해서 사용하는 것이기 때문이다.
그래도 차이점이라고 한다면 종자값을 설정할 수 있다는 것이다.
종자값이 같은 Random인스턴스들은 항상 난수를 같은 순서대로 반환한다.
public class Main {
public static void main(String[] args) {
Random rand = new Random(1);
Random rand2 = new Random(1);
System.out.println("= rand =");
for(int i=0; i<5; i++) {
System.out.println(i + ":" + rand.nextInt());
}
System.out.println();
System.out.println("= rand2 =");
for(int i=0; i<5; i++) {
System.out.println(i + ":" + rand2.nextInt());
}
}
}
//결과
= rand =
0:-1155869325
1:431529176
2:1761283695
3:1749940626
4:892128508
= rand2 =
0:-1155869325
1:431529176
2:1761283695
3:1749940626
4:892128508
같은 종자값을 갖는 Random인스턴스는 시스템이나 실행시간 등에 관계 없이 항상 같은 값을 같은 순서로 반환할 것을 보장한다.
2-3. 정규식(Regular Expression) - java.util.regex패키지
정규식이란 텍스트 데이터 중에서 우너하는 조건(패턴, pattern)과 일치하는 문자열을 찾아내기 위해 사용하는 것으로 미리 정의된 기호와 문자를 이용해서 작성한 문자열을 말한다.
public class Main {
public static void main(String[] args) {
String[] data = {"bat", "baby", "bonus", "cA", "ca", "co", "c.", "c0", "car", "combat", "count", "date", "disc"};
Pattern p = Pattern.compile("c[a-z]*");
for(int i=0; i<data.length; i++) {
Matcher m = p.matcher(data[i]);
if(m.matches()) {
System.out.print(data[i] + ",");
}
}
}
}
//결과
ca,co,car,combat,count,
Pattern은 정규식을 정의하는데 사용되고 Matcher는 정규식을 데이터와 비교하는 역할을 한다.
정규식의 일부를 괄호로 묶어서 그룹화할 수 있다.
public class Main {
public static void main(String[] args) {
String source = "HP:011-1111-1111, HOME:02-999-9999";
String pattern = "(0\\\\d{1,2})-(\\\\d{3,4})-(\\\\d{4})";
Pattern p = Pattern.compile(pattern);
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: 011-1111-1111 -> 011, 1111, 1111
2: 02-999-9999 -> 02, 999, 9999
(0\\\\d{1,2})-(\\\\d{3,4})-(\\\\d{4}) 는 괄호를 이용해서 정규식을 세 부분으로 나누었다.
group() 또는 group(0)은 그룹으로 매칭된 문자열 전체를 나누어지지 않은 채로 반환한다.
2-5. java.util.StringTokenizer클래스
StringTokenizer는 긴 문자열을 지정된 구분자를 기분으로 토큰(token)이라는 여러 개의 문자열로 잘라내는 데 사용된다.
그러나 StringTokenizer는 구분자로 단 하나의 문자밖에 사용하지 못하기 때문에 보다 복잡한 형태의 구분자로 문자열을 나누어야 할 때는 어쩔 수 없이 정규식을 사용하는 메서드를 사용해야 할 것이다.
💡 split()은 빈 문자열도 토큰으로 카운트된다는 차이점이 있다.
'Reading > 자바의 정석' 카테고리의 다른 글
Chapter 8. 예외처리 (1) | 2023.03.25 |
---|