-
🌑 java.lang package
-
✔️ Object class
-
✔️ String class
-
✔️ StringBuffer class & StringBuilder class
-
✔️ Math class
-
✔️ Wrapper class
-
🌒 유용한 클래스
-
✔️ java.util.Objects class
-
✔️ java.util.Random class
-
✔️ 정규식 ( Regular Expression ) - java.util.regex package
-
✔️ java.util.Scanner class
-
✔️ java.util.StringTokenizer class
-
✔️ java.math.BigInteger class
-
✔️ java.math.BigDecimal class
-
▼ Study📋
🌑 java.lang package
🔸 Java programming에 가장 기본이 되는 class들을 포함하고 있기 때문에
java.lang package의 class들은 import문 없이도 사용 가능하게 되어있다
✔️ Object class
🔸 모든 class의 최상위 super class이기 때문에
Object class의 멤버들은 모든 class에서 instance 생성 없이 바로 사용 가능
🔹equals(Object obj)
- 매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean값으로 알려주는 메서드
➠ 즉, 객체의 주소를 비교 ! (객체 ⭢ 같은 주소 X / 두 개 이상의 참조변수 ⭢ 같은 주소값 O) - String,Date,File,wrapper class는 equals 메서드를 그대로 사용하는 것이 아니라,
instance의 주소값이 아닌 instance의 멤버변수에 저장된 값을 비교하도록 Overriding 하여 사용!
- 의외로 StringBuffer class는 위의 class처럼 Overriding 하여 사용하지 않는다.
public boolean equals(Objec obj) { // Overriding
if(obj instance of ClassName)
return value == ((Person)obj).value;
else
return false;
}
ClassName(long value) {
this.value = value;
}
🔹hashCode()
- hashing 기법 (다량의 데이터를 저장 · 검색하는 데 유용)에 사용되는 'hash function'을 구현한 메서드
- hash function
: 찾고자하는 값을 입력 ⭢ 그 값이 저장된 위치를 알려주는 hash code 반환
- hash function
- class의 instance 변수에 저장된 값으로 비교해야 한다면 equals 메서드처럼 Overriding 필요 !
➠ hashing 기법을 사용하는 HashMap, HashSet 같은 class에 저장할 객체라면
반드시 hashCode 메서드를 Overriding 해야 한다 !
- 32bit JVM에서는 서로 다른 두 객체는 같은 hash code를 갖지 못하지만,
64bit JVM에서는 8 byte의 주소값으로 hash code(4 byte)를 만들기 때문에 중복 가능 !
- 32bit JVM에서는 서로 다른 두 객체는 같은 hash code를 갖지 못하지만,
- String class는 문자열의 내용이 같으면, 동일한 hash code를 반환하도록
hashCode 메서드가 Overriding되어 있다. - 주소값으로 hash code를 생성하기 때문에 모든 객체에 대해 항상 다른 hash code를 반환
( 다만, System.identityHashCode(Object x)의 호출결과는 실행할 때마다 달라질 수 있다. )
🔹toString()
- instance에 대한 정보를 문자열(String)로 제공할 목적으로 정의된 메서드
➠ instance의 정보 제공 → 대부분 instance 변수에 저장된 값들을 문자열로 표현한다는 말
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
🔻 toString 메서드를 Overriding 하지 않는다면, toString()을 호출했을 때
위처럼 class 이름과 16진수의 hash code를 반환한다.
- toString()은 일반적으로 instance나 class에 대한 정보 또는 instance 변수들의 값을
문자열로 변환하여 반환하도록 Overriding된다.
➠ Object class의 toString()의 접근 제어자는 public이므로,
이를 Overriding하는 class에서는 toString()의 접근제어자를 public으로 해야만 한다.
( Super class의 메서드의 접근 범위 ≤ Sub class에서 Overriding하는 메서드의 접근 범위 )
- String class의 toString()
➠ instance가 갖고 있는 문자열을 반환하도록 Overriding - Date class의 toString()
➠ instance가 갖고 있는 날짜와 시간을 문자열로 변환하여 반환하도록 Overriding
- String class의 toString()
🔹clone()
- 자신을 복제하여 새로운 instance를 생성하는 메서드
- Object class에 정의된 clone()은 단순히 instance 변수의 값만 복사
➠ 참조타입의 instance 변수가 있는 class는 완전한 instance 복제 X
➠ 예를 들어 배열의 경우, 복제된 instance도 같은 배열의 주소를 갖기 때문에
복제된 instance의 작업이 원래의 instance에도 영향을 미치게 된다.
➠ Overriding 필요 ( 보통 깊은 복사 (deep copy)가 가능하도록 Overriding )
- clone()을 사용하기 위한 조건
- 복제할 class가 Clonable interface(정의된 메서드가 없다)를 구현해야만 복제 가능
➠ instance의 데이터를 보호하기 위한 조치
➠ Clonable interface가 구현되어있다는 것 ⭢ 복제를 허용한다는걸 알려주는 정도 - clone()을 Overriding하면서 접근 제어자를 protected ⭢ public
➠ 상속관계가 없는 다른 class에서도 Overriding한 clone()을 호출하기 위해 !
- 복제할 class가 Clonable interface(정의된 메서드가 없다)를 구현해야만 복제 가능
class CloneOverridingClass implements Clonable {
...
public Object clone() {
Object obj = null;
try { // 반드시 예외처리를 해주어야 한다.
obj = super.clone();
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
class CloneEx {
public static void main(String[] args) {
// 생성자가 정의되어 있다고 가정
CloneOverridingClass original = new CloneOverridingClass(3, 5);
CloneOverridingClass copy = (CloneOverridingClass)original.clone(); // 형변환 필요
...
}
}
🔻 Object class의 clone() ⭢ Cloneable을 구현하지 않은 class 내에서 호출되면 예외 발생 !
class CloneOverridingClass implements Clonable {
...
public Object clone() {
Object obj = null;
obj = super.clone();
return obj;
}
}
class CloneEx {
public static void main(String[] args) {
try {
CloneOverridingClass original = new CloneOverridingClass(3, 5);
CloneOverridingClass copy = (CloneOverridingClass)original.clone();
...
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
🔻' throws '를 이용한 예외 처리
int[] arr = {1, 2, 3, 4, 5};
// Overriding한 clone()을 이용해서 배열을 복사하는 방법
int[] arrClone1 = (CloneOverridingClass)arr.clone();
// System.arraycopy()를 이용해서 배열을 복사하는 방법
int[] arrClone2 = new int[arr.length]; // 같은 길이의 배열 생성
System.arraycopy(ar, 0, arrClone, 0, arr.length); // 내용 복사
🔻 배열 뿐만 아니라 java.util package의 Vector, ArrayList, LinkedList, HashSet, TreeSet,
HashMap, TreeMap, Calendar, Date와 같은 class들이 이와 같은 방식으로 복제 가능 !
- 공변 반환타입 (covariant return type) ( JDK1.5 ~ )
➠ Overriding할 때, Super class의 메서드의 반환타입을 Sub class 타입으로 변경 가능
class CloneOverridingClass implements Clonable {
...
public ClassName clone() {
Object obj = null;
try { // 반드시 예외처리를 해주어야 한다.
obj = super.clone();
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return (ClassName)obj; // 반환타입을 Sub class의 타입으로 변경
}
}
class CloneEx {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
// int[] arrClone = (CloneOverridingClass)arr.clone();
int[] arrClone = arr.clone();
...
}
}
🔸 얕은 복사 (shallow copy) & 깊은 복사 (deep copy)
- shallow copy ( 원본 ⇄ 복사본 )
class Circle implements Clonable {
...
public Circle clone() { // shallow copy
Object obj = null;
try {
obj = super.clone(); // Super class인 Object class의 clone() 호출
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
🔻 단순히 object class의 clone() 호출만 하는 경우 ➠ 얕은 복사 (shallow copy))
- deep copy ( 원본 ⇎ 복사본 )
class Circle implements Clonable {
Point p;
double r;
Circle(Point p, double r) {
this.p = p;
this.r = r;
}
public Circle Clone() { // deep copy
Object obj = null;
try {
obj = super.clone(); // Super class인 Object class의 clone() 호출
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
Circle copyObj = (Circle)obj;
// 복제된 객체가 Point class의 새로운 instance 참조
copyObj.p = new Circle(this.p.x, this.p.y);
return copyObj;
}
...
}
class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
...
}
🔻 원본이 참조하고 있는 객체까지 복사 ➠ 깊은 복사 (deep copy)
🔹 getClass()
- 자신이 속한 class의 Class 객체를 반환하는 메서드
➠ Class class : class 이름이 ' Class '인 class
▪️ Class 객체- 해당 class의 모든 정보를 담고 있으며, class 당 1개만 존재
- class 파일이 ' 클래스 로더 (ClassLoader) '에 의해서 메모리에 올라갈 때, 자동 생성
▪️ ClassLoade- 실행 시에 필요한 class를 동적으로 메모리에 Load하고 변환하는 역할
- 기존에 생성된 class 객체가 메모리에 존재하는지 확인
- 존재하면 객체의 참조를 반환
존재하지 않으면, 클래스 패스(classpath)에 지정된 경로를 따라서 class 파일을 찾는다. - 찾으면 해당 class 파일을 읽어서 Class 객체로 변환
못 찾으면 ' ClassNotFoundException ' 발생
- 실행 시에 필요한 class를 동적으로 메모리에 Load하고 변환하는 역할
- class 파일을 읽어서 사용하기 편한 형태로 저장해놓은 것
➠ Class 객체
🔸 Class 객체를 얻는 방법
- Object.getClass()
- 정보를 얻으려는 class가 인스턴스화(instantiate)된 상태이어야 한다 !
AnyClass obj = new AnyClass();
// AnyClass obj = AnyClass.class.newInstance();
Class cObj = obj.getClass();
// Class cObj = new Card().getClass();
🔻 newInstance()는 ' InstantiationException ' 이 발생할 수 있으므로 예외처리가 필요 !
- * . class ( 가장 간단한 방법 )
- instance가 존재하지 않고, 컴파일된 class 파일만 있다면 리터럴로 Class 객체를 바로 얻을 수 있다 !
Class cObj = AnyClass.class;
- Class.forName()
➠ 런타임 동적 로딩 ( Run-time Dynamic Loading )
: 보통 다른 class 파일을 불러올 때는
컴파일 시 JVM의 Method Area에 class 파일이 같이 바인딩(binding)이 되지만,
forName()으로 class 파일을 불러올 때는
컴파일 시에 바인딩이 되지 않고 Run-time 때 불러오게 된다.
즉, JVM이 코드를 실행하다가 .class 파일을 로딩하는 것 !
+ ) cl Load-time Dynamic Loading
: JVM에 class 파일을 로딩할 때 필요한 다른 class를 동적으로 로딩하는 것
- 리터럴 방식처럼 컴파일된 class 파일이 있다면 class 이름만으로 Class 객체를 반환받을 수 있다.
➠ 단, 이때는 해당 class의 도메인(domain)을 상세히 적어줘야 한다. (오타 주의) - Class 객체를 찾지 못한다면 ' ClassNotFoundException '를 발생시켜 예외처리가 강제 !
- forName()을 통해 Class 객체를 얻게 되면, 위의 두 가지 방법보다 메모리를 절약하며
동적 로딩 (Dynamic Loading)을 할 수 있기 때문에 성능이 가장 좋다 ! - 특정 class 파일, 예를 들어 DB 드라이버를 메모리에 올릴 때 주로 사용 !
🔺Oracle, MySQL, SQL Server 등 여러 종류의 DB와 연동할 수 있는 시스템이 있다
➠ 이 시스템을 컴파일할 때 모든 DB의 라이브러리(드라이버)를 같이 컴파일할 필요 X
➠ 시스템을 구동할 때 어떤 DB와 연결할지 결정되면 드라이버만 Loading되면 되기 때문이다.
➠ 이럴 때, forName()을 이용하여 메모리를 절약하며 class를 동적 로딩하면 좋다 !
- 리터럴 방식처럼 컴파일된 class 파일이 있다면 class 이름만으로 Class 객체를 반환받을 수 있다.
Class cObj = Class.forName("AnyClass"); // 도메인 없이 class 이름만으로 Class 객체를 얻었다.
Class<?> cObj2 = Class.forName("java.lang.String"); // ? : Unbounded Wildcards
🔻 <?> : <? extends Object>의 줄임 표현, 메서드 매개변수의 자료형에 사용되는 제너릭
➠ 매개변수의 자료형을 Object class를 상속받은 class로만 제한
즉, 어떤 자료형의 객체도 매개변수로 받겠다는 의미
🔻Class<?>
➠ Class 객체는 어떤 타입으로도 선언 가능
- ' Class ' class의 메서드
➠ Class class엔 class의 정보를 얻을 수 있는 많은 수의 메서드가 정의되어 있다.
- String getName() : class의 이름을 리턴
- Package getPackage() : class의 package 정보를 package class 타입으로 리턴
- Field[] getFields() : public으로 선언된 변수 목록을 Field class 배열 타입으로 리턴
- Method[] getMethods() : public으로 선언된 메서 목록을 Method class 배열 타입으로 리턴
( 해당 클래스에서 사용 가능한 상속받은 메드도 포함 ) - Constructor[] getConstructors()
: 해당 class에 선언된 모든 public 생성자 정보를 Constructor 배열 타입으로 리턴 - int getModifiers() : 해당 클래스의 접근자(modifier) 정보를 int 타입으로 리턴
- String toString() : 해당 클래스 객체를 문자열로 리턴
✔️ String class
🔹 변경 불가능한 (immutable) class
- String class에는 문자열을 저장할 문자형 배열 참조변수(char[ ]) value를 instance 변수로 정의 !
public final class String implements java.io.Serializable, Comparable {
private char[] value;
...
}
🔻 String class는 final로 선언되어 있으므로 다른 class의 Super class가 될 수 없다.
- 한 번 생성된 String class의 instance가 갖고 있는 문자열은 읽어 올 수만 있고, 변경 불가능 !
➠ ' + ' 연산자를 이용해서 문자열을 결합하는 경우에 instance 내의 문자열이 바뀌는 것이 아니라
결합된 문자열이 담긴 새로운 instance가 생성되는 것이다.
➠ 문자열간의 결합이나 추출 등 문자열을 다루는 작업이 필요한 경우에는 String class 대신
새로운 instance 생성 없이 문자열 변경이 가능한 StringBuffer class를 사용하여 메모리공간을 아끼자 !
🔹 문자열의 비교
- 문자열 리터럴 (String literal)을 지정하여 문자열을 만드는 방법
String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
true
true
🔻 java 소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 class 파일에 저장된다.
➠ 문자열 리터럴도 String instance이기 때문에, 같은 내용의 문자열은 하나의 instance만 공유
➠ 즉, 같은 내용의 문자열 리터럴은 한 번만 저장된다 !
🔻 class 파일에는 소스파일이 포함된 모든 리터럴의 목록이 있는데
해당 class 파일이 ClassLoader에 의해 메모리에 올라갈 때,
이 리터럴의 목록에 있는 리터럴들이 JVM내에 있는 '상수 저장소 (constant pool)'에 저장된다.
➠ 이 때, 문자열 리터럴이 자동적으로 생성되어 저장되는 것 !
- String class 생성자 사용하여 문자열을 만드는 방법
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
false
true
🔹 빈 문자열 (Empty String)
- ' String s = ""; '과 같은 코드가 있을 때, 참조변수 s가 참조하고 있는 String class의 instance는
내부에
내부에 ' new char[0] ' 과 같이 길이가 0인 char형 배열을 저장하고 있는 것 !
➠ String과 달리 char형 변수에는 반드시 하나의 문자를 지정해야 한다.
String s = null;
char c = '\u0000'; // 유니코드의 첫 번째 문자로써 아무런 문자도 지정되지 않은 빈 문자
// char[] cArr = new char[0]; // char[] cArr = {};와 동일
// String s = new String(cArr); // String s = new String("");와 동일
String s = ""; // 빈 문자열로 초기화
char c = ' '; // 공백으로 초기화
- c언어는 문자열 끝에 null 문자가 항상 붙지만,
java에서는 문자열 끝에 null 문자가 붙지 않으며 문자열의 길이정보를 따로 저장 !
🔹 String class의 생성자 & 메서드
메서드 / 설명 | 코드 | 결과 |
String(String s) | String s = new String("Dog"); | s = "Dog" |
주어진 문자열(s)를 갖는 String class의 instance 생성 |
||
String(char[] value) | char[] c = ['D', 'o', 'g']; String s = new String(c); |
s = "Dog" |
주어진 문자열(value)를 갖는 String class의 instance 생성 |
||
String(StringBuffer buf) | StringBuffer sb = new StringBuffer("Dog"); String s = new String(sb); |
s = "Dog" |
StringBuffer class의 instance가 갖고 있는 문자열과 같은 내용의 String class의 instance 생성 |
||
char charAr(int index) | String s = "Dog"; char c = s.charAt(1); |
c = "o" |
지정된 위치(index)에 있는 문자를 반환 (index는 0부터 시작) |
||
int comparTo(String str) | int i = "aaa".compareTo("aaa"); int i2 = "aaa".compareTo("bbb"); int i3 = "bbb".compareTo("aaa"); |
i = 0 i2 = -1 i3 = 1 |
문자열(str)과 사전순서로 비교 같으면 0을, 이전이면 음수를, 이후면 양수 반환 |
||
String concat(String str) | String s = "Dog" String s2 = s.concat(" is sleeping"); |
s2 = "Dog is sleeping" |
문자열(str)을 뒤에 덧붙인다. | ||
boolean contains(charSequence s) | String s = "Dog"; boolean b = s.contains("Do"); |
b = true |
지정된 문자열(s)이 포함되었는지 검사 | ||
boolean endsWith(String suffix) | String file = "Dog.txt"; boolean b = file.endsWith("txt"); |
b = true |
지정된 문자열(suffix)로 끝나는지 검사 | ||
boolean equals(Object obj) | String s = "Dog"; boolean b = s.equals("Dog"); boolean b2 = s.equals("dog"); |
b = true b2 = false |
매개변수로 받은 문자열(obj)과 String class의 instance의 문자열을 비교 obj가 String이 아니거나 문자열이 다르면 false를 반환 |
||
boolean equalsIgnoreCase(String str) | String s = "Dog"; boolean b = s.equalsIgnoreCase("Dog"); boolean b2 = s.equalsIgnoreCase("dog"); |
b = true b2 = true |
문자열과 String class의 instance의 문자열을 대소문자 구분없이 비교 |
||
int indexOf(int ch) | String s = "Dog"; int idx1 = s.indexOf('o'); int idx2 = s.indexOf('k'); |
idx1 = 1 idx2 = -1 |
주어진 문자(ch)의 위치(index)를 반환 못 찾으면 -1을 반환 (index는 0부터 시작) |
||
int indexOf(int ch, int pos) | String s = "Dog"; int idx1 = s.indexOf('o', 0); int idx2 = s.indexOf('o', 2); |
idx1 = 1 idx2 = -1 |
지정된 위치(pos)부터 확인하여 주어진 문자(ch)의 위치(index)를 반환 못 찾으면 -1을 반환 (index는 0부터 시작) |
||
int indexOf(String str) | String s = "Dog"; int idx = s.indexOf("og"); |
idx = 1 |
주어진 문자열의 위치(index)를 반환 없으면 -1을 반환 (index는 0부터 시작) |
||
String intern() | String s = new String("Dog"); String s2 = new String("Dog"); boolean b = (s.intern() == s2.intern()); |
b = true |
문자열을 상수풀(constant pool)에 등록 이미 상수풀에 같은 문자열이 있을 경우 그 문자열의 주소값을 반환 |
||
int lastIndexOf(int ch) | String s = "Dog.Cat.Panda"; int idx = s.lastIndexOf('.'); |
idx = 7 |
주어진 문자 또는 문자코드를 문자열의 오른쪽 끝에서부터 찾아서 위치(index) 반환 못 찾으면 -1 반환 |
||
int lastIndexOf(String str) | String s = "Dog.Cat.Dog"; int idx = s.lastIndexOf("Dog"); |
idx = 8 |
주어진 문자열(str)을 instance의 문자열 끝에서부터 찾아서 위치(index) 반환 못 찾으면 -1 반환 |
||
int length() | String s = "Dog"; int length = s.length(); |
length = 3 |
문자열의 길이를 반환 | ||
String replace(char old, char nw) | String s = "Dog"; String s1 = s.replace('D', 'd'); |
s1 = "dog" |
문자열 중의 문자(old)를 새로운 문자(nw)로 바꾼 문자열을 반환 |
||
String replace(CharSequence old , CharSequence nw) |
String s = "DogDog"; String s1 = s.replace('Do', 'do'); |
s1 = "dogdog" |
문자열 중의 문자열(old)를 새로운 문자열(nw)로 모두 바꾼 문자열을 반환 |
||
String replaceAll(String regex , String replacement) |
String s = "DogDog"; String s1 = s.replaceAll('Dog', 'Cat'); |
s1 = "CatCat" |
문자열 중 주어진 문자열(regex)과 일치하는 것을 새로운 문자열(replacement)로 모두 바꾼 문자열을 반환 |
||
String replaceFirst(String regex , String replacement) |
String s = "DogDog"; String s1 = s.replaceFirst('Dog', 'Hot'); |
s1 = "HotDog" |
문자열 중 주어진 문자열(regex)과 일치하는 것 중, 첫 번째 것만 새로운 문자열(replacement)로 변경 후 반환 |
||
String[] split(String regex) | String animals = "dog, cat, panda"; String[] arr = animals.split(","); |
arr[0] = "dog" arr[1] = "cat" arr[2] = "panda" |
문자열을 주어진 분리자(regex)로 나누어 문자열 배열에 담아 반환 |
||
String[ ] split(String regex, int limit) | String animals = "dog, cat, panda"; String[] arr = animals.split(",", 2); |
arr[0] = "dog" arr[1] = "cat, panda" |
문자열을 주어진 분리자(regex)로 나누어 문자열 배열에 담아 반환 단, 주어진 수(limit)만큼만 자른다. |
||
boolean startsWith(String prefix) | String s = "dog, cat, panda"; boolean b = s.startsWith(dog); |
b = true |
주어진 문자열(prefix)로 시작하는지 검사 | ||
String substring(int begin) String substring(int begin, int end) |
String s = "Dog is barking!"; String c = s.substring(4); String p = s.substring(7, 14) |
c = "is barking!" p = "is barking" |
주어진 시작위치(begin)부터 끝 위치(end) 범위에 포함된 문자열을 얻는다. 이때, 끝 위치의 문자는 포함 X |
||
String toLowerCase() String toUpperCase() |
String s = "Dog, Cat, Panda"; String s1 = s.toLowerCase(); String s2 = s.toUpperCase(); |
s1 = "dog, cat, panda" s2 = "DOG, CAT, PANDA" |
String class의 instance에 저장되어있는 모든 문자열을 소문자/대문자로 변환하여 반환 |
||
String toString() | String s = "Dog, Cat, Panda"; String s1 = s.toString(); |
s1 = "Dog, Cat, Panda" |
String class의 instance에 저장된 문자열 반환 | ||
String trim() | String s = " Dog Cat "; String s1 = s.trim(); |
s1 = "Dog Cat" |
문자열의 왼쪽 끝과 오른쪽 끝에 있는 공백을 없앤 결과를 반환 이때 문자열의 중간에 있는 공백은 제거 X |
||
static String valueOf(...) | String b = String.valueOf(true); String c = String.valueOf('a'); String f = String.valueOf(10f); String d = String.valueOf(10.0); java.util.Date dd = new java.util.Date(); String date = String.valueOf(dd); |
b = "true" c = "a" f = "10.0" d = "10.0" date = "Thur Jan 29 19:30:00 KST 2023" |
매개변수의 값을 문자열로 변환하여 반환 참조변수의 경우, toString()을 호출한 결과 반환 |
🔹 join() & StringJoiner ( JDK1.8 ~ )
- join() : 여러 문자열 사이에 구분자를 넣어서 결합하는 메서드
⟺ split() : 구분자로 문자열을 자르는 메서드
String anmials = "dog,cat,squirrel";
String[] arr = animals.split(","); // 문자열을 ','를 구분자로 나눠서 배열에 저장
String str = String.join("-", arr); // 배열의 문자열을 '-'로 구분해서 결합
System.out.println(str); // dog-cat-squirrel
- StringJoiner
StringJoiner sj = new StringJoiner("," , "[" , "]");
String[] strArr = { "dog", "cat", "squirrel" };
for(String s : strArr)
sj.add(s.toUpperCase());
System.out.println(sj.toString()); // [dog,cat,squirrel]
🔹 Unicode ( JDK 1.5 ~ ) 의 보충문자
- String class의 메서드 중에 매개변수의 타입이 char가 아니라, int인 것들도 있다..
➠ 매개변수의 타입이 int인 것은 확장된 Unicode (보충 문자)를 다루기 위해서이다.
➠ 매개변수가 ' int ch '인 메서드 ⭢ 보충 문자(supplementary characters) 지원 O
/ 매개변수가 ' char ch '인 메서드 ⭢ 보충 문자(supplementary characters) 지원 X
🔹 문자 인코딩 변환
- getBytes(String charseName)
: 문자열의 문자 인코딩을 다른 인코딩으로 변경 - java ➠ UTF-16 사용 / 문자열 리터럴에 포함되는 문자 ➠ OS의 인코딩 사용 /
한글 Windows ➠ CP949 사용 - 서로 다른 문자 인코딩을 사용하는 컴퓨터 간에 데이터를 주고받을 때는 적절한 문자 인코딩 필요 !
public static void main(String[] args) throws Exception {
String str = "가";
byte[] bArr = str.getBytes("UTF-8"); // str을 UTF-8로 변환
byte[] bArr2 = str.getBytes("CP949"); // str을 CP949로 변환
System.out.println("UTF-8: " + joinByteArr(bArr));
System.out.println("CP949: " + joinByteArr(bArr2));
System.out.println("UTF-8: " + new String(bArr, "UTF-8"));
System.out.println("CP949: " + new String(bArr2, "CP949"));
}
static String joinByteArr(byte[] bArr) {
StringJoiner sj = new StringJoiner(":", "[", "]");
for(byte b : bArr)
sj.add(String.format("%02X", b);
return sj.toString();
}
UTF-8: [EA:B0:80]
CP949: [B0:A1]
UTF-8: 가
CP949: 가
🔻 ' 가 ' ➠ UTF-8 : '0xEAB080' (한글 한 글자 ⭢ 3 byte)
➠ CP949 : '0xB0A1' (한글 한 글자 ⭢ 2 btye)
🔹 String.format()
- printf() 사용법과 동일
String str = String.format("%d + %d = %d", 1, 2, 3);
System.out.println(str); // 1 + 2 = 3
🔹 기본형 값 ⇄ String
- 기본형 값 ⭢ String
- 빈 문자열 "" 더해주기
- valueOf() 사용 ➠성능향상
- String String.valueOf(boolean b)
- String String.valueOf(char c)
- String String.valueOf(int i)
➠ byte, short을 문자열로 변경할 때도 이 메서드를 사용 - String String.valueOf(long i)
- String String.valueOf(float f)
- String String.valueOf(double b)
- 참조변수에 String을 더하면, 참조변수가 가리키는
instance의 toString()을 호출하여 String을 얻은 다음 결합
int i = 1;
String str1 = i + "";
String str2 = String.valueOf(i);
- String ⭢ 기본형 값
- parseInt() 사용
- valueOf() 사용 ➠ parseInt()와 반환 타입만 다른 메서드
- boolean Boolean.parseBoolean(String s)
- byte Byte.parseBoolean(String s)
- short Short.parseBoolean(String s)
- int Integer.parseBoolean(String s)
- long Long.parseBoolean(String s)
- float Float.parseBoolean(String s)
- double Double.parseBoolean(String s)
- 기본형 타입의 이름의 첫 글자가 대문자인 것 ➠ Wrapper class
int i = Integer.parseInt("100");
int i2 = Integer.valueOf("100");
int val = Integer.parseInt(" 123 ".trim()); // 문자열 양 끝의 공백 제거 후 변환
🔻 valueOf()의 반환 타입은 int가 아닌 Integer !
➠ Auto-boxing에 의해 Integer가 int로 자동 변환
🔻 parseInt() 같은 메서드는 문자열에 공백 또는 문자가 포함되어 있는 경우,
변환 시 예외(NumberFormatException) 발생
➠ trim()을 사용하여 실수 방지 ! ( ' + (부호)', ' . (소수점)', ' f (float형)'와 같은 자료형 접미사는 허용 )
✔️ StringBuffer class & StringBuilder class
🔹 StringBuffer class
- String class처럼 instance가 생성될 때, 적절한 길이의 char형 배열이 생성되며
이 때 생성된 char형 배열을 instance 변수 ' value '가 참조하게 된다.
➠ 이 배열은 문자열을 저장하고 편집하기 위한 공간(Buffer)으로 사용된다.
public final class StringBuffer implements java.io.Serializable {
private char[] value;
...
}
- 내부적으로 문자열 편집을 위한 Buffer를 가지고 있으며,
StringBuffer class의 instance를 생성할 때 생성자 StringBuffer(int length)를 사용해 크기 지정 가능
public StringBuffer(int length) {
value = new char[length];
shared = false;
}
public StringBuffer() {
this(16); // Buffer의 크기를 지정하지 않으면, Buffer의 크기는 16이 된다.
}
public StringBuffer(String str) {
this(str.length() + 16);
append(str);
}
- Buffer의 길이를 충분히 여유있는 크기로 지정하지 않는다면,
Buffer의 길이를 늘려주는 작업을 추가로 수행하기 때문에, 작업 효율이 떨어진다.
(16개의 문자를 저장할 수 있는 크기의 버퍼를 추가로 생성)
➠ 배열의 길이는 변경이 불가능하므로 새로운 길이의 배열을 생성한 후에 이전 배열의 값을 복사 !
// 새로운 길이의 배열 생성
char newValue[] = new char[newCapacity];
// 배열 value의 내용을 배열 newValue로 복사
System.arraycopy(value, 0, newValue, 0, count); // count : 문자열의 길이
value = newValue; // 새로 생성된 배열의 주소를 참조변수 value에 저장
🔻 StringBuffer class의 instance 변수 ' value '는 길이가 증가된 새로운 배열을 참조
- StringBuffer의 변경
- 하나의 StringBuffer class의 instance에 대해 연속적으로 append() 호출 가능
StringBuffer sb = new StringBuffer("Squ");
sb.append("irr");
StringBuffer sb2 = sb.append("el");
System.out.println(sb);
System.out.println(sb2);
Squirrel
Squirrel
🔻 append()는 반환타입이 StringBuffer인데 자신의 주소를 반환하기에,
sb에는 새로운 문자열에 추가되면서 반환된 sb의 주소가 sb2에 저장된다.
➠ 따라서, 아래처럼 연속적으로 append()를 호출하는 것이 가능 !
StringBuffer sb = new StringBuffer("Squ");
sb.append("irr").append("el");
🔻 'ab.append("irr") '가 곧 ' sb '라고 할 수 있다.
- StringBuffer의 비교
- equals 메서드를 사용해도 등가비교연산자(==)한 것과 같은 결과를 얻는다 !
➠ String class와 달리 equals메서드가 문자열의 내용을 비교하도록 Overriding X
➠ Overriding되어 있는 toString()을 호출하여, String class의 instance를 얻은 다음
(StringBuffer의 내용을 String으로 변환) equals메서드를 사용하여 비교해야 한다 !
- equals 메서드를 사용해도 등가비교연산자(==)한 것과 같은 결과를 얻는다 !
// sb와 sb2에 같은 문자열에 담겨있다.
String s = sb.toString();
String s2 = sb2.toString();
System.out.println(sb.equals(sb2)); // false
System.out.println(s.equals(s2)); // true
🔹 StringBuffer class의 생성자 & 메서드
메서드 / 설명 | 코드 | 결과 |
StringBuffer() | StringBuffer sb = new StringBuffer(); | sb = "" |
16문자를 담을 수 있는 Buffer를 가진 StringBuffer class의 instance 생성 |
||
StringBuffer(int length) | StringBuffer sb = new StringBuffer(10); | sb = "" |
지정된 개수의 문자를 담을 수 있는 Buffer를 가진 StringBuffer class의 instance를 생성 | ||
StringBuffer(String str) | StringBuffer sb = new StringBuffer("Dog"); | sb = "Dog" |
지정된 문자열 값(str)을 갖는 StringBuffer class의 instance를 생성 |
||
StringBuffer append() | StringBuffer sb = new StringBuffer("Dog"); StringBuffer sb1 = sb.append(true); sb.append('c').append(10.0f); |
sb = "Dogtruec10.0" sb1 = "Dogtruec10.0" |
매개변수로 입력된 값을 문자열로 변환하여 StringBuffer class의 instance가 저장하고 있는 문자열의 뒤에 덧붙인다. |
||
int capacity() | StringBuffer sb = new StringBuffer(100); s.append("Dog"); int buffersize = sb.capacity(); |
buffersize = 100 |
StringBuffer class의 instance의 Buffer 크기 반환 | ||
int length() | StringBuffer sb = new StringBuffer(100); s.append("Dog"); int stringSize = sb.length(); |
stringSize = 3 |
Buffer에 담긴 문자열의 길이 반환 | ||
char charAt(int index) | StringBuffer sb = new StringBuffer("Dog"); char c = sb.charAt(2); |
c = 'g' |
지정된 위치(index)에 있는 문자 반환 | ||
StringBuffer deleteCharAt(int index) | StringBuffer sb = new StringBuffer("Dogg"); sb.deleteCharAt(3); |
sb = "Dog" |
지정된 위치(index)의 문자를 제거 | ||
StringBuffer insert(int pos, ...) | StringBuffer sb = new StringBuffer("Dog"); sb.insert(3, ','); sb.insert(4, "Cat"); |
sb = "Dog,Cat" |
두 번째 매개변수로 받은 값을 문자열로 변환하여 지정된 위치(pos)에 추가 (pos는 0부터 시작) |
||
StringBuffer replace(int start, intend, String str) | StringBuffer sb = new StringBuffer("0123456"); sb.replace(3, 6, "rep"); |
sb = "012rep6" |
지정된 범위(start~end)의 문자들을 주어진 문자열로 바꾼다. end 위치의 문자는 범위에 포함 X | ||
StringBuffer reverse() | StringBuffer sb = new StringBuffer("0123"); sb.reverse(); |
sb = "3210" |
StringBuffer class의 instance에 저장되어 있는 문자열의 순서를 거꾸로 나열 |
||
void setCharAt(int index, char ch) | StringBuffer sb = new StringBuffer("0123"); sb.setCharAt(3, 'S'); |
sb = "012S" |
지정된 위치의 문자를 주어진 문자(ch)로 변환 | ||
void setLength(int newLength) | StringBuffer sb = new StringBuffer("012345"); sb.setLength(3); StringBuffer sb2 = new StringBuffer("012345"); sb2.setLength(10); String str = sb2.toString().trim(); |
sb = "012" sb2 = "012345 " str = "012345" |
지정된 길이로 문자열의 길이를 변경 길이를 늘리는 경우엔 나머지 빈 공간을 NULL 문자 '\u0000'로 채운다. |
||
String toString() | StringBuffer sb = new StringBuffer("Dog"); String str = sb.toString(); |
str = "Dog" |
StringBuffer class의 instance의 문자열 ⭢ String | ||
String substring(int start) String substring(int start, int end) |
StringBuffer sb = new StringBuffer("012345"); String str = sb.substring(3); String str2 = sb.substring(3,4); |
str = "345" str = "3" |
지정된 범위 내의 문자열을 String으로 뽑아서 반환 끝위치(end)는 범위에 포함 X |
🔹 StringBuilder class
- StringBuffer class에서 쓰레드(Thread)의 동기화(Synchronization)만 빼고 같은 기능
➠ 멀티 쓰레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 성능만 저하시킨다 !
✔️ Math class
🔸 Math class의 생성자는 접근 제어자가 private !
- Math class 내에 instance 변수가 없어 instance를 생성할 필요 X
- Math class의 메서드는 모두 static이며,
자연로그의 밑과 원주율만 상수(public static final)로 정의되어 있다.
🔹 올림, 버림, 반올림
- 반올림
- round() ➠ 반환값이 long
- rint() ➠ 반환값이 double이며, 두 정수의 정가운데 있는 값은 가장 가까운 짝수 정수를 반환
- 올림
- ceil()
- 버림
- floor()
🔹 예외를 발생시키는 메서드
- 메서드 이름에 ' Exact '가 포함된 메서드 ( JDK1.8 ~ )
➠ 정수형 간의 연산에서 발생할 수 있는 Overflow를 감지하여 예외(ArithmeticException)을 발생시킨다.
int addExact(int x, int y) // x + y
int subtractExact(int x, int y) // x - y
int multiplyExact(int x, int y) // x * y
int incrementExact(int a) // a++
int decrementExatc(int a) // a--
int negateExact(int a) // -a 또는 '~a+1'
int toIntExact(long value) // (int)value - int로의 형변환
🔻 negateExcat(int a)
➠ 부호를 반대로 바꿔주는 식은 '~a + 1'인데, '~a'의 결과가 int의 최대값이면 Overflow가 발생하게 된다.
➠ 위처럼 Overflow가 발생한 경우,
try - catch 문으로 정수형 값을 long으로 형변환 해주는 예외처리가 필요 !
🔹 삼각함수 · 지수 · 로그
- sin(), cos(), tan() ➠ 매개변수의 단위는 '라디안(radian)'
- sqrt() ➠ 제곱근 / pow() ➠ 제곱
- toRadians() ➠ 라디안(radian) 단위의 값으로 변환
- '180/PI'를 곱해주거나 toDegrees() 이용 ➠ 도(degree) 단위의 값으로 변환
- logR+(R+)
🔹 StrictMath class
- Math class는 JVM이 설치된 OS의 메서드를 호출하여 사용
➠ OS에 의존적이므로 컴퓨터마다 결과가 다를 수 있다.
➠ 항상 같은 결과를 얻도록 성능을 다소 포기하여 작성한 class가 StrictMath class !
🔹 Math class의 메서드
메서드 | 기능 |
static double abs(double a) static float abs(float f) static int abs(int f) static long abs(long f) |
주어진 값의 절대값을 반환 |
static double ceil(double a) | 주어진 값을 올림하여 반환 |
static double floor(double a) | 주어진 값을 버림하여 반환 |
static double max(double a, double b) static float max(float a, float b) static int max(int a, int b) static long max(long a, long b) |
주어진 두 값을 비교하여 큰 쪽을 반환 |
static double min(double a, double b) static float min(float a, float b) static int min(int a, int b) static long min(long a, long b) |
주어진 두 값을 비교하여 작은 쪽을 반환 |
static double random() | 0.0~1.0 범위의 임의의 double값을 반환 (1.0은 범위에 포함 X) |
static double rint(double a) | 주어진 double값과 가장 가까운 정수값을 double형으로 반환 단, 두 정수의 가운데 있는 값(x.5)은 짝수를 반환 |
static long round(double a) static long round(float a) |
소수점 첫째자리에서 반올림한 정수값(long)을 반환 매개변수의 값이 음수인 경우, round()와 rint()읠 결과가 달라질 수 있다는 것에 주의 |
✔️ Wrapper class
🔸 객체지향 개념에서 모든 것은 객체로 다뤄져야 한다
- java가 완전한 객체지향 언어가 아닌 이유
➠ 8개의 기본형(primitive type)을 객체로 다루지 X ⭢ 대신 보다 높은 성능 ! - 기본형 변수를 객체로 다뤄야 하는 경우
ex) 매개변수로 객체를 요구, 기본형 값이 아닌 객체로 저장, 객체간의 비교가 필요할 때, etc.
➠ 기본형 값들을 객체로 변환하기 위해 사용되는 class가 Wrapper class !
Primitive type | Wrapper class | 생성자 |
boolean | Boolean | Booelan(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) |
🔻 Wrapper class의 생성자는 매개변수로 문자열(String)이나 각 자료형의 값들을 인자로 받는데,
생성자의 매개변수로 문자열을 제공할 때, 각 자료형에 맞는 문자열을 사용해야 한다!
ex) new Integer("1.0"); ➠ NumberFormatException 발생
- Overriding
- equals() ➠객체가 갖고 있는 값 비교
- toString ➠ 객체가 갖고 있는 값을 문자열로 변환하여 반환
- Auto-boxing이 된다고 해도 Integer class의 객체에 비교연산자 사용 불가능
➠ compareTo() 사용
🔹 Number class

- Number class
- Wrapper class type ➞ Primitive type
➠ 객체가 갖고 있는 값을 숫자와 관련된 기본형으로 변환하여 반환하는 메서드- intValue()
- longValue()
- floatValue()
- doubleValue()
- Wrapper class type ➞ Primitive type
- BigInteger class
- long으로도 다룰 수 없는 큰 범위의 정수를 처리하기 위한 class
- BigDecimal class
- double로도 다룰 수 없는 큰 범위의 부동 소수점수를 처리하기 위한 class
🔹 문자열 ➙ 숫자
int i = new Integer("100").intValue(); // floatValue(), longValue(), ...
int i2 = Integer.parseInt("100"); // Double.parseDouble("100"), ...
Integer i3 = Integer.valueOf("100"); // Long.valueOf("100"), ...
🔻 '타입.parse타입(String s)' 메서드 ➠ 반환값이 기본형(Primitive type)
'타입.valueOf()' 메서드 ➠ 반환값이 래퍼 클래스 타입(Wrapper class type)
int i3 = Integer.valueOf("100");
🔻 ' Autoboxing ' 기능( JDK1.5 ~ ) 때문에 반환값이 기본형일 때와 래퍼 클래스일 때의 차이가 사라졌다 !
➠ 성능은 조금 더 느리긴 하지만, 구별없이 valueOf()를 쓰는 것도 괜찮다.
static int parseInt(String s, int radix)
static Integer valueOf(String s, int radix)
int i = Integer.parseInt("100", 2); // 100(2) -> 4
🔻문자열이 10진수가 아닌 다른 진법(radix)의 숫자일 때도 변환이 가능하도록 하는 메서드
🔸 Autoboxing & Unboxing
- Autoboxing
- 기본형 값을 Wrapper class의 객체로 자동 변환시켜주는 것
int i = 1;
Integer iObj = new Integer(7);
int sum = i + iObj; // Error : 기본형과 참조형 간의 덧셈 불가 (JDK1.5 이전)
// Compile 후의 코드
int i = 1;
Integer iObj = new Integer(7);
int sum = i + iObj.intValue(); // 자동 형변환
- Unboxing (↔ Autoboxing)
- Wrapper class의 객체를 기본형 값으로 자동 변환 시켜주는 것
🔻 ArrayList에 숫자를 저장하거나 꺼낼 때 편리 !
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(10); // Autoboxing : 10 → new Integer(10)
int value = list.get(0); // Unboxing : new Integer(10) → 10
🔺 Autoboxing & Unboxing은 Compiler가 제공하는 편리한 기능일 뿐
기본형과 참조형 간의 형변환 · 연산이 불가능하다는 java의 원칙이 바뀐 것 X
➠ 생성자가 없는 class에 Compiler가 자동적으로 기본 생성자를 추가해 주듯이
개발자가 간략하게 쓴 구문을 Compiler가 원래의 구문으로 변경해주는 것 !
int i = 1;
Integer intg = (Integer) i;
Object obj = (Object) i;
Long lng = 100L;
// Compile 후
Integer intg = Integer.valueOf(i);
Object obj = (Object)Integer.valueOf(i);
Long lng = new Long(100L);
🌒 유용한 클래스
✔️ java.util.Objects class
🔹 Object의 보조 class
- Math class처럼 모든 메서드가 ' static '
static boolean isNull() // ⭤ nonNull()
🔻 해당 객체가 null이면 true 반환, 아니면 false
- requireNonNull()
static T requireNonNull(T obj)
static T requireNonNull(T obj, String message)
static T requireNonNull(T obj, Supplier messageSupplier)
🔻 해당 객체가 null인 경우, NullPointerException을 발생시킨다.
🔻 두 번째 매개변수로 지정하는 문자열(message)은 예외의 메시지가 된다.
➠ 매개변수의 유효성 검사 !
- compare()
static int compare(Object a, Object b, Comparator c)
🔻 두 비교대상이 같으면 0, 크면 양수, 작으면 음수를 반환한다.
- equals()
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
🔻 내부에서 a와 b의 null 검사를 하기 때문에,
Object class의 ' a.equals(b) '와 달리 null 검사를 위한 조건식을 따로 넣지 않아도 된다.
🔻 a와 b가 모두 null인 경우에는 참을 반환
- deepEquals()
static boolean deepEquals(Object a, Object b)
🔻 객체를 재귀적으로 비교하기 때문에, 다차원 배열의 비교도 가능 !
String[][] str2D = new String[][] {{"aaa", "bbb"}, {"AAA", "BBB"}};
String[][] str2D2 = new String[][] {{"aaa", "bbb"}, {"AAA", "BBB"}};
System.out.println(Objects.equals(str2D, str2D2));
System.out.println(Objects.deepEquals(str2D,str2D2));
false
true
➔ equals()로 2차원 이상의 문자열 배열 비교할 때엔 반복문을 함께 써야하지만,
deepEquals()는 반복문 없이 가능 !
- toString
static String toString(Obect o)
static String toString(Obect o, String nullDefault)
🔻 Object class의 ' toString() ' 메서드와 달리 내부적으로 null 검사 진행
🔻 ' o '가 null일 때, 두 번째 매개변수를 대신 사용할 값으로 지정 가능
- hashCode()
static int hashCode(Object o)
static int hashCode(Object... values)
🔻 내부적으로 null 검사 진행하고 Object class의 ' hashCode() ' 메서드를 호출
null일 때엔 0을 반환 !
🔻 보통 class에 선언된 instance의 변수들의 hashCode()를 조합해서 반환하도록 Overriding하지만,
그 대신 hashCode()매개변수의 타입이 가변인자 인 두 번째 메서드로 대체 가능 !
🔸 Objects class를 static import 하더라도 Object class의 메서드와
같은 이름의 메서드를 사용하면 Compiler가 구별하지 못해 충돌 발생 !
import static java.util.Objects.*;
...
// equals(str2D, str2D_2_; // Error !
Objects.equals(str2D, str2D_2_; // OK !
➔ 이러한 경우엔 class의 이름을 붙여줄 수밖에 없다 !
✔️ java.util.Random class
🔹 Math.random() & Random.next기본형타입(기본형타입 n)
- Math.random()도 내부적으로 Random class의 instance를 생성해서 사용하는 것으로 차이 X
int num = (int)(Math.random() * 6) + 1;
int num = new Random().nextInt(6) + 1; // nextInt(6) : 0 ~ 5 사이의 정수를 반환
- 종자값이 같은 Random class의instance들은 항상 같은 난수를 같은 순서대로 반환하기 때문에,
종자값(seed)를 설정할 수 있다는 점에서 차이가 있다 ! - ' nextInt(int n) ' 일 때, n은 범위에 포함 X
🔹 Random class의 생성자 & 메서드
Random() // 현재시간(System.currentTimeMillis())을
// 종자값(seed)으로 이용하는 Random class의 instance 생성
Rnadom(long seed) // 매개변수 seed를 종자값으로 하는 Random class의 instance 생성
void setSeed(long seed) // 종자값을 주어진 값(seed)로 변경
- 생성자 Random()은 위처럼 현재시간을 1/1000 단위로 변환하여 반환한 값을
종자값으로 설정하기 때문에 실행할 때마다 얻는 난수가 달라진다. - nextBytes()
➠ BigInteger(int signum, byte[] magnitude)와 함께 사용하면
int의 범위보다 더 넓은 범위의 난수를 얻을 수 있다.
🔸 Math.random()을 이용해서 만든 실제 프로그래밍에 유용할만한 메서드
- int[ ] fillRand(int[] arr, int from, int to)
: 배열 arr을 from과 to 범위의 값들로 채워서 반환 - int[ ] fillRand(int[ ] arr, int[ ] data)
: 배열 arr을 배열 data에 있는 값들로 채워서 반환 - int getRand(int from, int to)
: from과 to 범위의 정수(int)값을 반환하며, from과 to 모두 범위에 포함 !
public static int[] fillRand(int[] arr, int from, int to) {
for(int i = 0; i < arr.length; i++)
arr[i] = getRand(from, to);
return arr;
}
public static int[] fillRand(int[] arr, int[] data) {
for(int i = 0; i < arr.length; i++)
arr[i] = data[getRand(0, data.length -1)];
return arr;
}
public static int getRand(int from, int to) {
return (int) (Math.random() * (Math.abs(to-from) + 1))
+ Math.min(from, to); // from 값 반환
// abs : 절대값으로 반환하는 메서드
}
🔻 불연속적인 범위의 있는 값을 저장해서 임의로 얻어와야 하는 경우에
위의 메서드들이 유용하게 쓰인다.
ex ) Database에 넣은 테스트 데이터를 만드는 경우
✔️ 정규식 ( Regular Expression ) - java.util.regex package
🔹 text 데이터 중에서 원하는 pattern과 일치하는 문자열을 찾아내기 위해 사용하는 것
🔹 package ' regex '
- Pattern class
➠ 정규식을 정의하는 역할
Pattern p = Pattern.compile("c[a-z]*");
🔻 Pattern class의 정규식을 매개변수로 하는 static 메서드
Pattern compile(String regex) '를 호출하여 Pattern class의 instance를 생성
🔻 Pattern class 메서드
boolean matches(String regex, CharSequence input) // 정규표현식의 패턴과 일치하는지 체크
Pattern asPredicate() // 문자열을 일치시키는데 사용할 수 있는 술어를 작성
String pattern() // 컴파일된 정규표현식을 String 형태로 반환
Pattern split(CharSequence input) // 문자열을 주어진 인자값 CharSequence 패턴에 따라 분리
- Matcher class
➠ 정규식(pattern)을 데이터와 비교하는 역할 ( group() 메서드를 이용하면 출력도 가능 )
Matcher m = p.matcher(data[i]);
if(m.matches()) { ... }
🔻 Pattern class의 정규식으로 비교할 대상을 매개변수로 하는 메서드
' Matcher matcher(CharSequence input) '를 호출해서 Matcher class의 instance를 생성
➠ ' CharSequence ' 은 interface 타입이며, 다형성 기능을 통해
문자열 말고도 다양한 형태의 입력 데이터를 받을 수 있다.
( 이를 구현한 class는 CharBuffer, String, StringBuffer 가 있다. )
🔻 이렇게 생성된 instance로 ' boolean matches() '를 호출해서 정규식에 부합하는지 확인
🔻 Matcher class 메서드
find() // 패턴이 일치하는 경우 true, 불일치하는 경우 false 반환
// 반복 호출하는 경우, 이전에 발견한 부분의 다음부터 다시 패턴 매칭 시작
find(int start) // start 위치 이후부터 매칭검색
start() // 매칭되는 문자열의 시작위치 반환
start(int group) // 매개변수로 받은 group이 매칭되는 시작위치 반환
end() // 매칭되는 문자열의 끝위치의 다음 문자위치 반환
end(int group) // 매개변수로 받은 group이 매칭되는 끝위치의 다음 문자위치 반환
group() // group(0) : 그룹으로 매칭된 문자열을 전체를 나누어지지 않은 채로 반환
group(int group) // 그룹화되어 매칭된 패턴 중 group 번째 부분 반환
groupCount() // 괄호로 지정해서 그룹화한 패턴의 전체 개수 반환
matches() // 패턴이 전체 문자열과 일치할 경우 true 반환
// (일부 문자열이 아닌 전체 문자열과 완벽히 일치)
appendReplacement(StringBuffer sb, String replacement) // 패턴과 일치하는 문자열을 'replacement'로 대체하는 메서드
appendTail(StringBuffer sb) // 마지막으로 치환된 이후의 부분을 sb에 덧붙이는 메서드
➠ 그룹화 ( grouping ) : 정규식의 일부를 괄호로 나누어 묶을 수 있다.
➠ ' group(int i) '를 호출할 때 i가 실제 group의 수보다 많으면 예외 발생
( java.lang.IndexOutOfBoundsException )
🔹 String class의 정규식 메서드
➠ String 문자열에 바로 정규표현식을 적용하여 필터링 가능 !
- String.matches()
boolean matches(String regex)
🔻 매개변수로 주어진 정규식 ' regex '에 매칭되는 값이 있는지 확인
- String.replaceAll()
String replaceAll(String regex, String replacement)
🔻 문자열내에서 정규식 ' regex '과 매치되는 모든 문자열을
문자열 ' replacement '로 바꾼 문자열 반환
- String.split()
String[] split(String regex)
🔻 매개변수로 주어진 정규식 ' regex '과 매치되는 문자열을 구분자로 하여 분할
🔹 정규식 문법 기호 모음
- 정규식 기본 기호
기호 | 설명 |
. | 임의의 문자 1개를 의미 |
^ | 시작을 의미 ⭢ ^a : a로 시작하는 단어 단, [ ] 괄호 안에 있다면 일치하지 않다는 부정의 의미 ⭢ [^a-z] : a부터 z까지를 제외한 모든 문자 |
$ | $ 앞의 문자열로 문자가 끝나는 단어 |
[ ] | [ ] 괄호 안의 문자가 있는지 확인 ⭢ [ab][cd] : a,b 중 한 문자와 c,d 중 한 문 |
- | 사이의 문자 혹은 숫자를 의미 ⭢ [a-z0-9] : 알파벳 소문자 전체, 0-9 중 한 문자 |
| | ' 또는 ' 을 의미 ⭢ [a|b] : a 또는 b |
( ) | 그룹(group)을 의미 ⭢ 01(0|1) : "01" 뒤에 "0" 또는 "1"이 들어다 |
{ } | 개수를 의미 ⭢ a{3}b : "a"가 3번 온 후 "b"가 온다 |
\b | 공백, tap, ",", "/" 등을 의미 ⭢ dog\b : dog cat (o) , dog/ (o) , dog. (x) |
\B | \b 와 반대의 의미 공백, tap, ",", "/" 등이 아닌 문자인 경우 매치 |
\d | 0~9 사이의 숫자 ( = ' [0-9] ' ) |
\D | \d 와 반대의 의미 숫자가 아닌 어떤 문자 ⭢ [^0-9] |
\s | 공백, 탭 |
\S | 공백, 탭이 아닌 문자 |
\w | 알파벳 대소문자, 숫자, "_" ⭢ [a-zA-Z_0-9] |
\W | \w 와 반대의 의미 ⭢ [^a-zA-z_0-9] |
- 정규식 수량 기호
기호 | 설명 |
? | 앞의 표현식이 없거나 최대 한 개만 있을 수 있다는 의미 |
* | 앞의 표현식이 있을 수도 없을 수도 있다는 의미 |
+ | 앞의 표현식이 1개 이상 있다는 의미 |
{n} | 앞의 표현식이 딱 n개 있다는 의미 |
{n,m} | 앞의 표현식이 n개 이상 m 개 이하로 있다는 의미 |
{n,} | 앞의 표현식이 n개 이상 있다는 의미 |
- 자주 사용되는 정규식 샘플
^[0-9]*$ // 숫자
^[a-zA-Z]*$ // 영문자
^[가-힣]*$ // 한글
\\w+@\\w+\\.\\w+(\\.\\w+)? // E-Mail
^\d{2,3}-\d{3,4}-\d{4}$ // 전화번호
^01(?:0|1|[6-9])-(?:\d{3}|\d{4})-\d{4}$ // 휴대전화번호
\d{6}\-[1-4]\d{6} // 주민등록번호
^d{3}-\d{2}$ // 우편번호
✔️ java.util.Scanner class
🔹Scanner class
- 다양한 입력 소스로부터 데이터를 읽어오는 목적으로 정의된 class
Scanner(String source)
Scanner(File source)
Scanner(InputStream source)
Scanner(Readable source)
Scanner(ReadableByteChannel source)
Scanner(Path source) // JDK.17 ~
Scanner useDelimiter(Pattern pattern)
Scanner usdDelimiter(String pattern)
🔻 위와 같은 생성자를 통해 화면, 파일, 문자열과 같은 입력소스로부터 문자데이터를 읽어올 수 있다.
Scanner sc = new Scanner(new File("data.txt");
int cnt = 0;
int totalSum = 0;
while(sc.hasNextLine()) {
String line = sc.nextLine();
Scanner sc2 = new Scanner(line).useDelimiter(",");
int sum = 0;
while(sc2.hasNextInt()) {
sum += sc2.nextInt();
}
System.out.println(line + ", sum = " + sum);
totalSum += sum;
cnt++;
}
System.out.println("Line: " + cnt + ", Total: " + totalSum);
🔻 정규식 표현을 이용한 라인단위의 검색을 지원하며
구분자에도 정규식 표현을 사용할 수 있어서 복잡한 형태의 구분자도 처리 가능
🔹 Scanner class의 메서드
boolean nextBoolean()
byte nextByte()
short nextShort()
int nextInt()
long nextLong()
double nextDouble()
float nextFloat()
String nextLine()
🔻 이와 같은 메서드 덕분에 입력받은 문자를 다시 변환하는 작업 X
➠ 입력된 데이터의 형식에 맞는 메서드를 사용하지 않으면 inputMismatchException 발생
✔️ java.util.StringTokenizer class
🔹 StringTokenizer class
- 긴 문자열을 지정된 구분자를 기준으로 토큰(token)이라는 여러 개의 문자열로 잘라내는 데 사용
- String split(String regex)이나 Scanner useDelimiter(String pattern)을 사용해도 되지만,
StringTokenizer는 정규식 표현을 사용하지 않고 사용 가능 !
🔹 StringTokenizer의 생성자 & 메서드
- StringTokenizer()
StringTokenizer(String str, Strin delim) // 구분자 ≠ token
StringTokenizer(String str, String delim, boolean returnDelims)
// returnDelims = true 인 경우, 구분자 = token
- countToken()
int countToken()
🔻 전체 토큰(token)의 수 반환
- hasMoreTokens()
boolean hasMoreTokens()
🔻 토큰(token)이 남아있는지를 체크하고 결과를 반환
- nextToken()
String nextToken()
🔻 다음 토큰(token)을 반환
✔️ java.math.BigInteger class
🔹 BigInteger class
- long(정수를 표현할 수 있는 가장 큰 타입)으로 표현할 수 있는 범위(약 19자리)를 넘어서면
모두 0으로 출력되는 상황 발생
➠ BigInteger는 저장할 수 있는 숫자의 범위가 무한하다!
➠ 내부적으로 int 배열을 사용해서 값을 다루기 때문이다. ( 성능은 long 타입보다 떨어진다. )
final int signum; // 부호 : 1(양수), 0, -1(음수)
final int[] mag; // 값(magnitude)
🔻 String처럼 BigDecimal도 불변(immutable)이며, 값을 '2의 보수' 형태로 표현
🔹 BigInteger class의 생성자
BigInteger val;
val = new BigInteger("1234567890"); // 문자열로 생성
val = new BigInteger("FFFF", 16); // n진수(radix)의 문자열로 생성
val = BigInteger.valueOf(123456890L); // 숫자로 생성
🔻 생성자를 호출할 때, 인자 값으로 문자열을 넘겨주어야 한다.
🔻 n진수(radix)의 문자열로도 생성 가능
🔻 valueOf 메서드(static)를 이용하면 숫자로 초기화 가능
🔹 BigInteger class의 메서드 ( 연산 )
BigInteger add(BigInteger val) // 덧셈 (this + val)
BigInteger subtract(BigInteger val) // 뺄셈 (this - val)
BigInteger multiply(BigInteger val) // 곱셍 (this * val)
BigInteger divide(BigInteger val) // 나눗셈 (this / val)
BigInteger remainder(BigInteger val) // 나머지 (this % val)
🔻 BigInteger는 불변이므로, 반환타입이 BigInteger ➔ 새로운 instance가 반환된다는 의미 !
🔹 비트 연산 메서드
int bitCount() // 2진수로 표현했을 때, 1의 개수(음수는 0의 개수)를 반환
int bitLength() // 2진수로 표현했을 때, 값을 표현하는데 필요한 bit 수
boolean testBit(int n) // 우측에서 n+1번째 비트가 1이면 true, 0이면 false
BigInteger setBit(int n) // 우측에서 n+1번째 비트를 1로 변경
BigInteger clearBit(int n) // 우측에서 n+1번째 비트를 0으로 변경
BigInteger flipBit(int n) // 우측에서 n+1번째 비트를 번환 (1 → 0, 0 → 1)
🔻 큰 숫자를 다루기 위한 class인 만큼 성능을 향상시키기 위해 비트 단위로도 연산 수행
BigInteger bi = new BigInteger("4");
if(bi.remainder(new BigInteger("2")).equals(BigInteger.ZERO)) {
...
BigInteger bi = new BigInteger("4");
if(!bi.testBit(0)) { // 제일 오른쪽 비트가 0인지 확인
...
🔻 비트 연산 메서드를 사용하면 더 효율적인 코드 작성 가능
✔️ java.math.BigDecimal class
🔹 BigDecimal class
- double(소수점을 표현할 수 있는 가장 큰 타입)은 소수점의 정밀도에 있어서 한계(최대 13자리)가
존재하여 값이 유실되는 상황 발생 ( double은 내부적으로 수를 저장할 때 2진수의 근사치를 저장 )
➠ BigDecimal은 속도가 느리긴 해도 수를 10진수 형태로 저장하여 거의 무한한 정밀도 보장
private final BigInteger intVal; // 정수 (unscaled value)
private final int scale; // 지수 (scale)
private transient int precision; // 정밀도 (precision) - 정수의 자릿수
🔻BigInteger처럼 BigDecimal도 불변(immutable) !
🔹 BigDecimal class의 생성자
BigDecimal val;
val = new BigDecimal("123.4567890"); // 문자열로 생성
val = new BigDecimal(123.456); // double 타입의 리터럴로 생성
val = new BigDecimal(123456); // int, long 타입의 리터럴로 생성 가능
val = BigDecimal.valueOf(123.456); // 생성자 대신 valueOf(double) 사용
val = BigDecimal.valueOf(123456); // 생성자 대신 valueOf(int) 사용
🔻 생성자를 호출할 때, 보통 인자 값으로 문자열을 넘겨준다.
➠ double 타입의 값을 인자 값으로 넘겨주면 오차 발생
🔻 valueOf 메서드(static)를 이용하면 숫자로 초기화 가능
🔹 다른 타입으로의 변환
String toPlainString() // 어떤 경우에도 다른 기호없이 숫자로만 표현
String toString() // 필요하면 지수형태로 표현 가능
🔻 BigDecimal을 문자열로 변환하는 메서드
🔻 '1.0e-22'와 같은 지수 형태의 리터럴을 사용한 경우 다른 문자열을 반환할 수도 있다.
int intValue()
long longValue()
float floatValue()
double doubleValue()
🔻 Number로부터 상속받은 기본형으로 변환하는 메서드
byte byteValueExact()
short shortValueExact()
int intValueExact()
long longValueExact()
BigInteger toBigInterExact()
🔻정수형으로 변환하는 메서드 중에서 이름 끝에 ' Exact '가 붙은 메서드들은
변환환 결과가 변환한 타입의 범위에 속하지 않으면 예외(ArithmeticException)을 발생시킨다.
🔹 BigDecimal의 연산
BigDecimal add(BigDecimal val) // 덧셈 (this + val)
BigDecimal subtract(BigDecimal val) // 뺄셈 (this - val)
BigDecimal multiply(BigDecimal val) // 곱셍 (this * val)
BigDecimal divide(BigDecimal val) // 나눗셈 (this / val)
BigDecimal remainder(BigDecimal val) // 나머지 (this % val)
🔻 BigDecimal도 불변이므로, 반환타입이 BigDecimal ➔ 새로운 instance가 반환된다는 의미 !
🔻 단, 연산결과의 정수, 지수, 정밀도는 달라질 수 있다 !
🔹 반올림 모드 - divide( ) & setScale( )
BigDecimal divide(BigDecimal divisor)
BigDecimal divide(BigDecimal divisor, int rouningMode)
BigDecimal divide(BigDecimal divisor, RoundingMode rouningMode)
BigDecimal divide(BigDecimal divisor, int scale, int rouningMode)
BigDecimal divide(BigDecimal divisor, int scale, RoundingMode rouningMode)
BigDecimal divide(BigDecimal divisor, MathContext mc)
🔻 열거형 RoundingMode를 사용하여 BigDecimal의 나눗셈 결과를
반올림(roundingMode) 처리해준다 !
열거형 RoundingMode에 정의된 상수 |
설명 |
CEILING | 올림 |
FLOOR | 내림 |
UP | 양수일 때는 올림, 음수일 때는 내림 |
DOWN | 양수일 때는 내림, 음수일 때는 올림 |
HALF_UP | 일반적인 반올림 |
HALF_EVEN | 반올림 자리의 값이 짝수면 HALF_DOWN, 홀수면 HALF_UP |
HALF_DOWN | 6이상 올림, 6미만 버림 |
UNNECESSARY | 나눗셈의 결과가 딱 떨어지는 수 X ➔ ArithmeticException |
BigDecimal bigd = new BigDecimal("1.0");
BigDecimal bigd2 = new BigDecimal("3.0");
System.out.println(bigd.divide(bigd2)); // ArithmeticException 발생
System.out.println(bigd.divide(bigd2, 3, RoundingMode.HALF_UP)); // 0.333
🔻 divide()의 결과가 무한소수인 경우, RoundingMode를 지정해주지 않으면 예외 발생
🔹 java.math.MathContext
- RoundingMode와 precision(정밀도)를 하나로 묶어 놓은 class
- recision ➠ 정수와 소수점 이하를 모두 포함한 자리수를 의미
BigDecimal bigd = new BigDecimal("123.456");
BigDecimal bigd2 = new BigDecimal("1.0");
System.out.println(bigd.divide(bigd2, 2, RoundingMode.HALF_UP)); // 123.45
System.out.println(bigd.divide(bigd2, new MathContext(2, HALF_UP)); // 1.2E+2
🔹 scale의 변경
BigDecimal setScale(int newScale)
BigDecimal setScale(int newScale, int roundingMode)
BigDecimal setScale(int newScale, RoundingMode mode)
🔻 setScale()로 scale 값 줄이는 것 = 10의 n제곱으로 나누는 것
🔻 이때도 RoundingMode를 지정해주어야 한다!
🔸 돈과 관련된 프로그램을 개발하거나 알고리즘 문제를 풀 때 최악의 상황을
고려해야 할 경우, BigInteger class와 BigDecimal class 활용은 필수 !
▼ Study📋
☑️ 코드 스니펫 ( Code Snippet ) : 재사용 가능한 소스 코드
☑️ text 데이터를 다룰 때, 정규 표현식 ( Regular Expression )을 사용하자
🌑 java.lang package
🔸 Java programming에 가장 기본이 되는 class들을 포함하고 있기 때문에
java.lang package의 class들은 import문 없이도 사용 가능하게 되어있다
✔️ Object class
🔸 모든 class의 최상위 super class이기 때문에
Object class의 멤버들은 모든 class에서 instance 생성 없이 바로 사용 가능
🔹equals(Object obj)
- 매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean값으로 알려주는 메서드
➠ 즉, 객체의 주소를 비교 ! (객체 ⭢ 같은 주소 X / 두 개 이상의 참조변수 ⭢ 같은 주소값 O) - String,Date,File,wrapper class는 equals 메서드를 그대로 사용하는 것이 아니라,
instance의 주소값이 아닌 instance의 멤버변수에 저장된 값을 비교하도록 Overriding 하여 사용!
- 의외로 StringBuffer class는 위의 class처럼 Overriding 하여 사용하지 않는다.
public boolean equals(Objec obj) { // Overriding
if(obj instance of ClassName)
return value == ((Person)obj).value;
else
return false;
}
ClassName(long value) {
this.value = value;
}
🔹hashCode()
- hashing 기법 (다량의 데이터를 저장 · 검색하는 데 유용)에 사용되는 'hash function'을 구현한 메서드
- hash function
: 찾고자하는 값을 입력 ⭢ 그 값이 저장된 위치를 알려주는 hash code 반환
- hash function
- class의 instance 변수에 저장된 값으로 비교해야 한다면 equals 메서드처럼 Overriding 필요 !
➠ hashing 기법을 사용하는 HashMap, HashSet 같은 class에 저장할 객체라면
반드시 hashCode 메서드를 Overriding 해야 한다 !
- 32bit JVM에서는 서로 다른 두 객체는 같은 hash code를 갖지 못하지만,
64bit JVM에서는 8 byte의 주소값으로 hash code(4 byte)를 만들기 때문에 중복 가능 !
- 32bit JVM에서는 서로 다른 두 객체는 같은 hash code를 갖지 못하지만,
- String class는 문자열의 내용이 같으면, 동일한 hash code를 반환하도록
hashCode 메서드가 Overriding되어 있다. - 주소값으로 hash code를 생성하기 때문에 모든 객체에 대해 항상 다른 hash code를 반환
( 다만, System.identityHashCode(Object x)의 호출결과는 실행할 때마다 달라질 수 있다. )
🔹toString()
- instance에 대한 정보를 문자열(String)로 제공할 목적으로 정의된 메서드
➠ instance의 정보 제공 → 대부분 instance 변수에 저장된 값들을 문자열로 표현한다는 말
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
🔻 toString 메서드를 Overriding 하지 않는다면, toString()을 호출했을 때
위처럼 class 이름과 16진수의 hash code를 반환한다.
- toString()은 일반적으로 instance나 class에 대한 정보 또는 instance 변수들의 값을
문자열로 변환하여 반환하도록 Overriding된다.
➠ Object class의 toString()의 접근 제어자는 public이므로,
이를 Overriding하는 class에서는 toString()의 접근제어자를 public으로 해야만 한다.
( Super class의 메서드의 접근 범위 ≤ Sub class에서 Overriding하는 메서드의 접근 범위 )
- String class의 toString()
➠ instance가 갖고 있는 문자열을 반환하도록 Overriding - Date class의 toString()
➠ instance가 갖고 있는 날짜와 시간을 문자열로 변환하여 반환하도록 Overriding
- String class의 toString()
🔹clone()
- 자신을 복제하여 새로운 instance를 생성하는 메서드
- Object class에 정의된 clone()은 단순히 instance 변수의 값만 복사
➠ 참조타입의 instance 변수가 있는 class는 완전한 instance 복제 X
➠ 예를 들어 배열의 경우, 복제된 instance도 같은 배열의 주소를 갖기 때문에
복제된 instance의 작업이 원래의 instance에도 영향을 미치게 된다.
➠ Overriding 필요 ( 보통 깊은 복사 (deep copy)가 가능하도록 Overriding )
- clone()을 사용하기 위한 조건
- 복제할 class가 Clonable interface(정의된 메서드가 없다)를 구현해야만 복제 가능
➠ instance의 데이터를 보호하기 위한 조치
➠ Clonable interface가 구현되어있다는 것 ⭢ 복제를 허용한다는걸 알려주는 정도 - clone()을 Overriding하면서 접근 제어자를 protected ⭢ public
➠ 상속관계가 없는 다른 class에서도 Overriding한 clone()을 호출하기 위해 !
- 복제할 class가 Clonable interface(정의된 메서드가 없다)를 구현해야만 복제 가능
class CloneOverridingClass implements Clonable {
...
public Object clone() {
Object obj = null;
try { // 반드시 예외처리를 해주어야 한다.
obj = super.clone();
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
class CloneEx {
public static void main(String[] args) {
// 생성자가 정의되어 있다고 가정
CloneOverridingClass original = new CloneOverridingClass(3, 5);
CloneOverridingClass copy = (CloneOverridingClass)original.clone(); // 형변환 필요
...
}
}
🔻 Object class의 clone() ⭢ Cloneable을 구현하지 않은 class 내에서 호출되면 예외 발생 !
class CloneOverridingClass implements Clonable {
...
public Object clone() {
Object obj = null;
obj = super.clone();
return obj;
}
}
class CloneEx {
public static void main(String[] args) {
try {
CloneOverridingClass original = new CloneOverridingClass(3, 5);
CloneOverridingClass copy = (CloneOverridingClass)original.clone();
...
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
🔻' throws '를 이용한 예외 처리
int[] arr = {1, 2, 3, 4, 5};
// Overriding한 clone()을 이용해서 배열을 복사하는 방법
int[] arrClone1 = (CloneOverridingClass)arr.clone();
// System.arraycopy()를 이용해서 배열을 복사하는 방법
int[] arrClone2 = new int[arr.length]; // 같은 길이의 배열 생성
System.arraycopy(ar, 0, arrClone, 0, arr.length); // 내용 복사
🔻 배열 뿐만 아니라 java.util package의 Vector, ArrayList, LinkedList, HashSet, TreeSet,
HashMap, TreeMap, Calendar, Date와 같은 class들이 이와 같은 방식으로 복제 가능 !
- 공변 반환타입 (covariant return type) ( JDK1.5 ~ )
➠ Overriding할 때, Super class의 메서드의 반환타입을 Sub class 타입으로 변경 가능
class CloneOverridingClass implements Clonable {
...
public ClassName clone() {
Object obj = null;
try { // 반드시 예외처리를 해주어야 한다.
obj = super.clone();
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return (ClassName)obj; // 반환타입을 Sub class의 타입으로 변경
}
}
class CloneEx {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
// int[] arrClone = (CloneOverridingClass)arr.clone();
int[] arrClone = arr.clone();
...
}
}
🔸 얕은 복사 (shallow copy) & 깊은 복사 (deep copy)
- shallow copy ( 원본 ⇄ 복사본 )
class Circle implements Clonable {
...
public Circle clone() { // shallow copy
Object obj = null;
try {
obj = super.clone(); // Super class인 Object class의 clone() 호출
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
🔻 단순히 object class의 clone() 호출만 하는 경우 ➠ 얕은 복사 (shallow copy))
- deep copy ( 원본 ⇎ 복사본 )
class Circle implements Clonable {
Point p;
double r;
Circle(Point p, double r) {
this.p = p;
this.r = r;
}
public Circle Clone() { // deep copy
Object obj = null;
try {
obj = super.clone(); // Super class인 Object class의 clone() 호출
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
Circle copyObj = (Circle)obj;
// 복제된 객체가 Point class의 새로운 instance 참조
copyObj.p = new Circle(this.p.x, this.p.y);
return copyObj;
}
...
}
class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
...
}
🔻 원본이 참조하고 있는 객체까지 복사 ➠ 깊은 복사 (deep copy)
🔹 getClass()
- 자신이 속한 class의 Class 객체를 반환하는 메서드
➠ Class class : class 이름이 ' Class '인 class
▪️ Class 객체- 해당 class의 모든 정보를 담고 있으며, class 당 1개만 존재
- class 파일이 ' 클래스 로더 (ClassLoader) '에 의해서 메모리에 올라갈 때, 자동 생성
▪️ ClassLoade- 실행 시에 필요한 class를 동적으로 메모리에 Load하고 변환하는 역할
- 기존에 생성된 class 객체가 메모리에 존재하는지 확인
- 존재하면 객체의 참조를 반환
존재하지 않으면, 클래스 패스(classpath)에 지정된 경로를 따라서 class 파일을 찾는다. - 찾으면 해당 class 파일을 읽어서 Class 객체로 변환
못 찾으면 ' ClassNotFoundException ' 발생
- 실행 시에 필요한 class를 동적으로 메모리에 Load하고 변환하는 역할
- class 파일을 읽어서 사용하기 편한 형태로 저장해놓은 것
➠ Class 객체
🔸 Class 객체를 얻는 방법
- Object.getClass()
- 정보를 얻으려는 class가 인스턴스화(instantiate)된 상태이어야 한다 !
AnyClass obj = new AnyClass();
// AnyClass obj = AnyClass.class.newInstance();
Class cObj = obj.getClass();
// Class cObj = new Card().getClass();
🔻 newInstance()는 ' InstantiationException ' 이 발생할 수 있으므로 예외처리가 필요 !
- * . class ( 가장 간단한 방법 )
- instance가 존재하지 않고, 컴파일된 class 파일만 있다면 리터럴로 Class 객체를 바로 얻을 수 있다 !
Class cObj = AnyClass.class;
- Class.forName()
➠ 런타임 동적 로딩 ( Run-time Dynamic Loading )
: 보통 다른 class 파일을 불러올 때는
컴파일 시 JVM의 Method Area에 class 파일이 같이 바인딩(binding)이 되지만,
forName()으로 class 파일을 불러올 때는
컴파일 시에 바인딩이 되지 않고 Run-time 때 불러오게 된다.
즉, JVM이 코드를 실행하다가 .class 파일을 로딩하는 것 !
+ ) cl Load-time Dynamic Loading
: JVM에 class 파일을 로딩할 때 필요한 다른 class를 동적으로 로딩하는 것
- 리터럴 방식처럼 컴파일된 class 파일이 있다면 class 이름만으로 Class 객체를 반환받을 수 있다.
➠ 단, 이때는 해당 class의 도메인(domain)을 상세히 적어줘야 한다. (오타 주의) - Class 객체를 찾지 못한다면 ' ClassNotFoundException '를 발생시켜 예외처리가 강제 !
- forName()을 통해 Class 객체를 얻게 되면, 위의 두 가지 방법보다 메모리를 절약하며
동적 로딩 (Dynamic Loading)을 할 수 있기 때문에 성능이 가장 좋다 ! - 특정 class 파일, 예를 들어 DB 드라이버를 메모리에 올릴 때 주로 사용 !
🔺Oracle, MySQL, SQL Server 등 여러 종류의 DB와 연동할 수 있는 시스템이 있다
➠ 이 시스템을 컴파일할 때 모든 DB의 라이브러리(드라이버)를 같이 컴파일할 필요 X
➠ 시스템을 구동할 때 어떤 DB와 연결할지 결정되면 드라이버만 Loading되면 되기 때문이다.
➠ 이럴 때, forName()을 이용하여 메모리를 절약하며 class를 동적 로딩하면 좋다 !
- 리터럴 방식처럼 컴파일된 class 파일이 있다면 class 이름만으로 Class 객체를 반환받을 수 있다.
Class cObj = Class.forName("AnyClass"); // 도메인 없이 class 이름만으로 Class 객체를 얻었다.
Class<?> cObj2 = Class.forName("java.lang.String"); // ? : Unbounded Wildcards
🔻 <?> : <? extends Object>의 줄임 표현, 메서드 매개변수의 자료형에 사용되는 제너릭
➠ 매개변수의 자료형을 Object class를 상속받은 class로만 제한
즉, 어떤 자료형의 객체도 매개변수로 받겠다는 의미
🔻Class<?>
➠ Class 객체는 어떤 타입으로도 선언 가능
- ' Class ' class의 메서드
➠ Class class엔 class의 정보를 얻을 수 있는 많은 수의 메서드가 정의되어 있다.
- String getName() : class의 이름을 리턴
- Package getPackage() : class의 package 정보를 package class 타입으로 리턴
- Field[] getFields() : public으로 선언된 변수 목록을 Field class 배열 타입으로 리턴
- Method[] getMethods() : public으로 선언된 메서 목록을 Method class 배열 타입으로 리턴
( 해당 클래스에서 사용 가능한 상속받은 메드도 포함 ) - Constructor[] getConstructors()
: 해당 class에 선언된 모든 public 생성자 정보를 Constructor 배열 타입으로 리턴 - int getModifiers() : 해당 클래스의 접근자(modifier) 정보를 int 타입으로 리턴
- String toString() : 해당 클래스 객체를 문자열로 리턴
✔️ String class
🔹 변경 불가능한 (immutable) class
- String class에는 문자열을 저장할 문자형 배열 참조변수(char[ ]) value를 instance 변수로 정의 !
public final class String implements java.io.Serializable, Comparable {
private char[] value;
...
}
🔻 String class는 final로 선언되어 있으므로 다른 class의 Super class가 될 수 없다.
- 한 번 생성된 String class의 instance가 갖고 있는 문자열은 읽어 올 수만 있고, 변경 불가능 !
➠ ' + ' 연산자를 이용해서 문자열을 결합하는 경우에 instance 내의 문자열이 바뀌는 것이 아니라
결합된 문자열이 담긴 새로운 instance가 생성되는 것이다.
➠ 문자열간의 결합이나 추출 등 문자열을 다루는 작업이 필요한 경우에는 String class 대신
새로운 instance 생성 없이 문자열 변경이 가능한 StringBuffer class를 사용하여 메모리공간을 아끼자 !
🔹 문자열의 비교
- 문자열 리터럴 (String literal)을 지정하여 문자열을 만드는 방법
String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
true
true
🔻 java 소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 class 파일에 저장된다.
➠ 문자열 리터럴도 String instance이기 때문에, 같은 내용의 문자열은 하나의 instance만 공유
➠ 즉, 같은 내용의 문자열 리터럴은 한 번만 저장된다 !
🔻 class 파일에는 소스파일이 포함된 모든 리터럴의 목록이 있는데
해당 class 파일이 ClassLoader에 의해 메모리에 올라갈 때,
이 리터럴의 목록에 있는 리터럴들이 JVM내에 있는 '상수 저장소 (constant pool)'에 저장된다.
➠ 이 때, 문자열 리터럴이 자동적으로 생성되어 저장되는 것 !
- String class 생성자 사용하여 문자열을 만드는 방법
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
false
true
🔹 빈 문자열 (Empty String)
- ' String s = ""; '과 같은 코드가 있을 때, 참조변수 s가 참조하고 있는 String class의 instance는
내부에
내부에 ' new char[0] ' 과 같이 길이가 0인 char형 배열을 저장하고 있는 것 !
➠ String과 달리 char형 변수에는 반드시 하나의 문자를 지정해야 한다.
String s = null;
char c = '\u0000'; // 유니코드의 첫 번째 문자로써 아무런 문자도 지정되지 않은 빈 문자
// char[] cArr = new char[0]; // char[] cArr = {};와 동일
// String s = new String(cArr); // String s = new String("");와 동일
String s = ""; // 빈 문자열로 초기화
char c = ' '; // 공백으로 초기화
- c언어는 문자열 끝에 null 문자가 항상 붙지만,
java에서는 문자열 끝에 null 문자가 붙지 않으며 문자열의 길이정보를 따로 저장 !
🔹 String class의 생성자 & 메서드
메서드 / 설명 | 코드 | 결과 |
String(String s) | String s = new String("Dog"); | s = "Dog" |
주어진 문자열(s)를 갖는 String class의 instance 생성 |
||
String(char[] value) | char[] c = ['D', 'o', 'g']; String s = new String(c); |
s = "Dog" |
주어진 문자열(value)를 갖는 String class의 instance 생성 |
||
String(StringBuffer buf) | StringBuffer sb = new StringBuffer("Dog"); String s = new String(sb); |
s = "Dog" |
StringBuffer class의 instance가 갖고 있는 문자열과 같은 내용의 String class의 instance 생성 |
||
char charAr(int index) | String s = "Dog"; char c = s.charAt(1); |
c = "o" |
지정된 위치(index)에 있는 문자를 반환 (index는 0부터 시작) |
||
int comparTo(String str) | int i = "aaa".compareTo("aaa"); int i2 = "aaa".compareTo("bbb"); int i3 = "bbb".compareTo("aaa"); |
i = 0 i2 = -1 i3 = 1 |
문자열(str)과 사전순서로 비교 같으면 0을, 이전이면 음수를, 이후면 양수 반환 |
||
String concat(String str) | String s = "Dog" String s2 = s.concat(" is sleeping"); |
s2 = "Dog is sleeping" |
문자열(str)을 뒤에 덧붙인다. | ||
boolean contains(charSequence s) | String s = "Dog"; boolean b = s.contains("Do"); |
b = true |
지정된 문자열(s)이 포함되었는지 검사 | ||
boolean endsWith(String suffix) | String file = "Dog.txt"; boolean b = file.endsWith("txt"); |
b = true |
지정된 문자열(suffix)로 끝나는지 검사 | ||
boolean equals(Object obj) | String s = "Dog"; boolean b = s.equals("Dog"); boolean b2 = s.equals("dog"); |
b = true b2 = false |
매개변수로 받은 문자열(obj)과 String class의 instance의 문자열을 비교 obj가 String이 아니거나 문자열이 다르면 false를 반환 |
||
boolean equalsIgnoreCase(String str) | String s = "Dog"; boolean b = s.equalsIgnoreCase("Dog"); boolean b2 = s.equalsIgnoreCase("dog"); |
b = true b2 = true |
문자열과 String class의 instance의 문자열을 대소문자 구분없이 비교 |
||
int indexOf(int ch) | String s = "Dog"; int idx1 = s.indexOf('o'); int idx2 = s.indexOf('k'); |
idx1 = 1 idx2 = -1 |
주어진 문자(ch)의 위치(index)를 반환 못 찾으면 -1을 반환 (index는 0부터 시작) |
||
int indexOf(int ch, int pos) | String s = "Dog"; int idx1 = s.indexOf('o', 0); int idx2 = s.indexOf('o', 2); |
idx1 = 1 idx2 = -1 |
지정된 위치(pos)부터 확인하여 주어진 문자(ch)의 위치(index)를 반환 못 찾으면 -1을 반환 (index는 0부터 시작) |
||
int indexOf(String str) | String s = "Dog"; int idx = s.indexOf("og"); |
idx = 1 |
주어진 문자열의 위치(index)를 반환 없으면 -1을 반환 (index는 0부터 시작) |
||
String intern() | String s = new String("Dog"); String s2 = new String("Dog"); boolean b = (s.intern() == s2.intern()); |
b = true |
문자열을 상수풀(constant pool)에 등록 이미 상수풀에 같은 문자열이 있을 경우 그 문자열의 주소값을 반환 |
||
int lastIndexOf(int ch) | String s = "Dog.Cat.Panda"; int idx = s.lastIndexOf('.'); |
idx = 7 |
주어진 문자 또는 문자코드를 문자열의 오른쪽 끝에서부터 찾아서 위치(index) 반환 못 찾으면 -1 반환 |
||
int lastIndexOf(String str) | String s = "Dog.Cat.Dog"; int idx = s.lastIndexOf("Dog"); |
idx = 8 |
주어진 문자열(str)을 instance의 문자열 끝에서부터 찾아서 위치(index) 반환 못 찾으면 -1 반환 |
||
int length() | String s = "Dog"; int length = s.length(); |
length = 3 |
문자열의 길이를 반환 | ||
String replace(char old, char nw) | String s = "Dog"; String s1 = s.replace('D', 'd'); |
s1 = "dog" |
문자열 중의 문자(old)를 새로운 문자(nw)로 바꾼 문자열을 반환 |
||
String replace(CharSequence old , CharSequence nw) |
String s = "DogDog"; String s1 = s.replace('Do', 'do'); |
s1 = "dogdog" |
문자열 중의 문자열(old)를 새로운 문자열(nw)로 모두 바꾼 문자열을 반환 |
||
String replaceAll(String regex , String replacement) |
String s = "DogDog"; String s1 = s.replaceAll('Dog', 'Cat'); |
s1 = "CatCat" |
문자열 중 주어진 문자열(regex)과 일치하는 것을 새로운 문자열(replacement)로 모두 바꾼 문자열을 반환 |
||
String replaceFirst(String regex , String replacement) |
String s = "DogDog"; String s1 = s.replaceFirst('Dog', 'Hot'); |
s1 = "HotDog" |
문자열 중 주어진 문자열(regex)과 일치하는 것 중, 첫 번째 것만 새로운 문자열(replacement)로 변경 후 반환 |
||
String[] split(String regex) | String animals = "dog, cat, panda"; String[] arr = animals.split(","); |
arr[0] = "dog" arr[1] = "cat" arr[2] = "panda" |
문자열을 주어진 분리자(regex)로 나누어 문자열 배열에 담아 반환 |
||
String[ ] split(String regex, int limit) | String animals = "dog, cat, panda"; String[] arr = animals.split(",", 2); |
arr[0] = "dog" arr[1] = "cat, panda" |
문자열을 주어진 분리자(regex)로 나누어 문자열 배열에 담아 반환 단, 주어진 수(limit)만큼만 자른다. |
||
boolean startsWith(String prefix) | String s = "dog, cat, panda"; boolean b = s.startsWith(dog); |
b = true |
주어진 문자열(prefix)로 시작하는지 검사 | ||
String substring(int begin) String substring(int begin, int end) |
String s = "Dog is barking!"; String c = s.substring(4); String p = s.substring(7, 14) |
c = "is barking!" p = "is barking" |
주어진 시작위치(begin)부터 끝 위치(end) 범위에 포함된 문자열을 얻는다. 이때, 끝 위치의 문자는 포함 X |
||
String toLowerCase() String toUpperCase() |
String s = "Dog, Cat, Panda"; String s1 = s.toLowerCase(); String s2 = s.toUpperCase(); |
s1 = "dog, cat, panda" s2 = "DOG, CAT, PANDA" |
String class의 instance에 저장되어있는 모든 문자열을 소문자/대문자로 변환하여 반환 |
||
String toString() | String s = "Dog, Cat, Panda"; String s1 = s.toString(); |
s1 = "Dog, Cat, Panda" |
String class의 instance에 저장된 문자열 반환 | ||
String trim() | String s = " Dog Cat "; String s1 = s.trim(); |
s1 = "Dog Cat" |
문자열의 왼쪽 끝과 오른쪽 끝에 있는 공백을 없앤 결과를 반환 이때 문자열의 중간에 있는 공백은 제거 X |
||
static String valueOf(...) | String b = String.valueOf(true); String c = String.valueOf('a'); String f = String.valueOf(10f); String d = String.valueOf(10.0); java.util.Date dd = new java.util.Date(); String date = String.valueOf(dd); |
b = "true" c = "a" f = "10.0" d = "10.0" date = "Thur Jan 29 19:30:00 KST 2023" |
매개변수의 값을 문자열로 변환하여 반환 참조변수의 경우, toString()을 호출한 결과 반환 |
🔹 join() & StringJoiner ( JDK1.8 ~ )
- join() : 여러 문자열 사이에 구분자를 넣어서 결합하는 메서드
⟺ split() : 구분자로 문자열을 자르는 메서드
String anmials = "dog,cat,squirrel";
String[] arr = animals.split(","); // 문자열을 ','를 구분자로 나눠서 배열에 저장
String str = String.join("-", arr); // 배열의 문자열을 '-'로 구분해서 결합
System.out.println(str); // dog-cat-squirrel
- StringJoiner
StringJoiner sj = new StringJoiner("," , "[" , "]");
String[] strArr = { "dog", "cat", "squirrel" };
for(String s : strArr)
sj.add(s.toUpperCase());
System.out.println(sj.toString()); // [dog,cat,squirrel]
🔹 Unicode ( JDK 1.5 ~ ) 의 보충문자
- String class의 메서드 중에 매개변수의 타입이 char가 아니라, int인 것들도 있다..
➠ 매개변수의 타입이 int인 것은 확장된 Unicode (보충 문자)를 다루기 위해서이다.
➠ 매개변수가 ' int ch '인 메서드 ⭢ 보충 문자(supplementary characters) 지원 O
/ 매개변수가 ' char ch '인 메서드 ⭢ 보충 문자(supplementary characters) 지원 X
🔹 문자 인코딩 변환
- getBytes(String charseName)
: 문자열의 문자 인코딩을 다른 인코딩으로 변경 - java ➠ UTF-16 사용 / 문자열 리터럴에 포함되는 문자 ➠ OS의 인코딩 사용 /
한글 Windows ➠ CP949 사용 - 서로 다른 문자 인코딩을 사용하는 컴퓨터 간에 데이터를 주고받을 때는 적절한 문자 인코딩 필요 !
public static void main(String[] args) throws Exception {
String str = "가";
byte[] bArr = str.getBytes("UTF-8"); // str을 UTF-8로 변환
byte[] bArr2 = str.getBytes("CP949"); // str을 CP949로 변환
System.out.println("UTF-8: " + joinByteArr(bArr));
System.out.println("CP949: " + joinByteArr(bArr2));
System.out.println("UTF-8: " + new String(bArr, "UTF-8"));
System.out.println("CP949: " + new String(bArr2, "CP949"));
}
static String joinByteArr(byte[] bArr) {
StringJoiner sj = new StringJoiner(":", "[", "]");
for(byte b : bArr)
sj.add(String.format("%02X", b);
return sj.toString();
}
UTF-8: [EA:B0:80]
CP949: [B0:A1]
UTF-8: 가
CP949: 가
🔻 ' 가 ' ➠ UTF-8 : '0xEAB080' (한글 한 글자 ⭢ 3 byte)
➠ CP949 : '0xB0A1' (한글 한 글자 ⭢ 2 btye)
🔹 String.format()
- printf() 사용법과 동일
String str = String.format("%d + %d = %d", 1, 2, 3);
System.out.println(str); // 1 + 2 = 3
🔹 기본형 값 ⇄ String
- 기본형 값 ⭢ String
- 빈 문자열 "" 더해주기
- valueOf() 사용 ➠성능향상
- String String.valueOf(boolean b)
- String String.valueOf(char c)
- String String.valueOf(int i)
➠ byte, short을 문자열로 변경할 때도 이 메서드를 사용 - String String.valueOf(long i)
- String String.valueOf(float f)
- String String.valueOf(double b)
- 참조변수에 String을 더하면, 참조변수가 가리키는
instance의 toString()을 호출하여 String을 얻은 다음 결합
int i = 1;
String str1 = i + "";
String str2 = String.valueOf(i);
- String ⭢ 기본형 값
- parseInt() 사용
- valueOf() 사용 ➠ parseInt()와 반환 타입만 다른 메서드
- boolean Boolean.parseBoolean(String s)
- byte Byte.parseBoolean(String s)
- short Short.parseBoolean(String s)
- int Integer.parseBoolean(String s)
- long Long.parseBoolean(String s)
- float Float.parseBoolean(String s)
- double Double.parseBoolean(String s)
- 기본형 타입의 이름의 첫 글자가 대문자인 것 ➠ Wrapper class
int i = Integer.parseInt("100");
int i2 = Integer.valueOf("100");
int val = Integer.parseInt(" 123 ".trim()); // 문자열 양 끝의 공백 제거 후 변환
🔻 valueOf()의 반환 타입은 int가 아닌 Integer !
➠ Auto-boxing에 의해 Integer가 int로 자동 변환
🔻 parseInt() 같은 메서드는 문자열에 공백 또는 문자가 포함되어 있는 경우,
변환 시 예외(NumberFormatException) 발생
➠ trim()을 사용하여 실수 방지 ! ( ' + (부호)', ' . (소수점)', ' f (float형)'와 같은 자료형 접미사는 허용 )
✔️ StringBuffer class & StringBuilder class
🔹 StringBuffer class
- String class처럼 instance가 생성될 때, 적절한 길이의 char형 배열이 생성되며
이 때 생성된 char형 배열을 instance 변수 ' value '가 참조하게 된다.
➠ 이 배열은 문자열을 저장하고 편집하기 위한 공간(Buffer)으로 사용된다.
public final class StringBuffer implements java.io.Serializable {
private char[] value;
...
}
- 내부적으로 문자열 편집을 위한 Buffer를 가지고 있으며,
StringBuffer class의 instance를 생성할 때 생성자 StringBuffer(int length)를 사용해 크기 지정 가능
public StringBuffer(int length) {
value = new char[length];
shared = false;
}
public StringBuffer() {
this(16); // Buffer의 크기를 지정하지 않으면, Buffer의 크기는 16이 된다.
}
public StringBuffer(String str) {
this(str.length() + 16);
append(str);
}
- Buffer의 길이를 충분히 여유있는 크기로 지정하지 않는다면,
Buffer의 길이를 늘려주는 작업을 추가로 수행하기 때문에, 작업 효율이 떨어진다.
(16개의 문자를 저장할 수 있는 크기의 버퍼를 추가로 생성)
➠ 배열의 길이는 변경이 불가능하므로 새로운 길이의 배열을 생성한 후에 이전 배열의 값을 복사 !
// 새로운 길이의 배열 생성
char newValue[] = new char[newCapacity];
// 배열 value의 내용을 배열 newValue로 복사
System.arraycopy(value, 0, newValue, 0, count); // count : 문자열의 길이
value = newValue; // 새로 생성된 배열의 주소를 참조변수 value에 저장
🔻 StringBuffer class의 instance 변수 ' value '는 길이가 증가된 새로운 배열을 참조
- StringBuffer의 변경
- 하나의 StringBuffer class의 instance에 대해 연속적으로 append() 호출 가능
StringBuffer sb = new StringBuffer("Squ");
sb.append("irr");
StringBuffer sb2 = sb.append("el");
System.out.println(sb);
System.out.println(sb2);
Squirrel
Squirrel
🔻 append()는 반환타입이 StringBuffer인데 자신의 주소를 반환하기에,
sb에는 새로운 문자열에 추가되면서 반환된 sb의 주소가 sb2에 저장된다.
➠ 따라서, 아래처럼 연속적으로 append()를 호출하는 것이 가능 !
StringBuffer sb = new StringBuffer("Squ");
sb.append("irr").append("el");
🔻 'ab.append("irr") '가 곧 ' sb '라고 할 수 있다.
- StringBuffer의 비교
- equals 메서드를 사용해도 등가비교연산자(==)한 것과 같은 결과를 얻는다 !
➠ String class와 달리 equals메서드가 문자열의 내용을 비교하도록 Overriding X
➠ Overriding되어 있는 toString()을 호출하여, String class의 instance를 얻은 다음
(StringBuffer의 내용을 String으로 변환) equals메서드를 사용하여 비교해야 한다 !
- equals 메서드를 사용해도 등가비교연산자(==)한 것과 같은 결과를 얻는다 !
// sb와 sb2에 같은 문자열에 담겨있다.
String s = sb.toString();
String s2 = sb2.toString();
System.out.println(sb.equals(sb2)); // false
System.out.println(s.equals(s2)); // true
🔹 StringBuffer class의 생성자 & 메서드
메서드 / 설명 | 코드 | 결과 |
StringBuffer() | StringBuffer sb = new StringBuffer(); | sb = "" |
16문자를 담을 수 있는 Buffer를 가진 StringBuffer class의 instance 생성 |
||
StringBuffer(int length) | StringBuffer sb = new StringBuffer(10); | sb = "" |
지정된 개수의 문자를 담을 수 있는 Buffer를 가진 StringBuffer class의 instance를 생성 | ||
StringBuffer(String str) | StringBuffer sb = new StringBuffer("Dog"); | sb = "Dog" |
지정된 문자열 값(str)을 갖는 StringBuffer class의 instance를 생성 |
||
StringBuffer append() | StringBuffer sb = new StringBuffer("Dog"); StringBuffer sb1 = sb.append(true); sb.append('c').append(10.0f); |
sb = "Dogtruec10.0" sb1 = "Dogtruec10.0" |
매개변수로 입력된 값을 문자열로 변환하여 StringBuffer class의 instance가 저장하고 있는 문자열의 뒤에 덧붙인다. |
||
int capacity() | StringBuffer sb = new StringBuffer(100); s.append("Dog"); int buffersize = sb.capacity(); |
buffersize = 100 |
StringBuffer class의 instance의 Buffer 크기 반환 | ||
int length() | StringBuffer sb = new StringBuffer(100); s.append("Dog"); int stringSize = sb.length(); |
stringSize = 3 |
Buffer에 담긴 문자열의 길이 반환 | ||
char charAt(int index) | StringBuffer sb = new StringBuffer("Dog"); char c = sb.charAt(2); |
c = 'g' |
지정된 위치(index)에 있는 문자 반환 | ||
StringBuffer deleteCharAt(int index) | StringBuffer sb = new StringBuffer("Dogg"); sb.deleteCharAt(3); |
sb = "Dog" |
지정된 위치(index)의 문자를 제거 | ||
StringBuffer insert(int pos, ...) | StringBuffer sb = new StringBuffer("Dog"); sb.insert(3, ','); sb.insert(4, "Cat"); |
sb = "Dog,Cat" |
두 번째 매개변수로 받은 값을 문자열로 변환하여 지정된 위치(pos)에 추가 (pos는 0부터 시작) |
||
StringBuffer replace(int start, intend, String str) | StringBuffer sb = new StringBuffer("0123456"); sb.replace(3, 6, "rep"); |
sb = "012rep6" |
지정된 범위(start~end)의 문자들을 주어진 문자열로 바꾼다. end 위치의 문자는 범위에 포함 X | ||
StringBuffer reverse() | StringBuffer sb = new StringBuffer("0123"); sb.reverse(); |
sb = "3210" |
StringBuffer class의 instance에 저장되어 있는 문자열의 순서를 거꾸로 나열 |
||
void setCharAt(int index, char ch) | StringBuffer sb = new StringBuffer("0123"); sb.setCharAt(3, 'S'); |
sb = "012S" |
지정된 위치의 문자를 주어진 문자(ch)로 변환 | ||
void setLength(int newLength) | StringBuffer sb = new StringBuffer("012345"); sb.setLength(3); StringBuffer sb2 = new StringBuffer("012345"); sb2.setLength(10); String str = sb2.toString().trim(); |
sb = "012" sb2 = "012345 " str = "012345" |
지정된 길이로 문자열의 길이를 변경 길이를 늘리는 경우엔 나머지 빈 공간을 NULL 문자 '\u0000'로 채운다. |
||
String toString() | StringBuffer sb = new StringBuffer("Dog"); String str = sb.toString(); |
str = "Dog" |
StringBuffer class의 instance의 문자열 ⭢ String | ||
String substring(int start) String substring(int start, int end) |
StringBuffer sb = new StringBuffer("012345"); String str = sb.substring(3); String str2 = sb.substring(3,4); |
str = "345" str = "3" |
지정된 범위 내의 문자열을 String으로 뽑아서 반환 끝위치(end)는 범위에 포함 X |
🔹 StringBuilder class
- StringBuffer class에서 쓰레드(Thread)의 동기화(Synchronization)만 빼고 같은 기능
➠ 멀티 쓰레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 성능만 저하시킨다 !
✔️ Math class
🔸 Math class의 생성자는 접근 제어자가 private !
- Math class 내에 instance 변수가 없어 instance를 생성할 필요 X
- Math class의 메서드는 모두 static이며,
자연로그의 밑과 원주율만 상수(public static final)로 정의되어 있다.
🔹 올림, 버림, 반올림
- 반올림
- round() ➠ 반환값이 long
- rint() ➠ 반환값이 double이며, 두 정수의 정가운데 있는 값은 가장 가까운 짝수 정수를 반환
- 올림
- ceil()
- 버림
- floor()
🔹 예외를 발생시키는 메서드
- 메서드 이름에 ' Exact '가 포함된 메서드 ( JDK1.8 ~ )
➠ 정수형 간의 연산에서 발생할 수 있는 Overflow를 감지하여 예외(ArithmeticException)을 발생시킨다.
int addExact(int x, int y) // x + y
int subtractExact(int x, int y) // x - y
int multiplyExact(int x, int y) // x * y
int incrementExact(int a) // a++
int decrementExatc(int a) // a--
int negateExact(int a) // -a 또는 '~a+1'
int toIntExact(long value) // (int)value - int로의 형변환
🔻 negateExcat(int a)
➠ 부호를 반대로 바꿔주는 식은 '~a + 1'인데, '~a'의 결과가 int의 최대값이면 Overflow가 발생하게 된다.
➠ 위처럼 Overflow가 발생한 경우,
try - catch 문으로 정수형 값을 long으로 형변환 해주는 예외처리가 필요 !
🔹 삼각함수 · 지수 · 로그
- sin(), cos(), tan() ➠ 매개변수의 단위는 '라디안(radian)'
- sqrt() ➠ 제곱근 / pow() ➠ 제곱
- toRadians() ➠ 라디안(radian) 단위의 값으로 변환
- '180/PI'를 곱해주거나 toDegrees() 이용 ➠ 도(degree) 단위의 값으로 변환
- logR+(R+)
🔹 StrictMath class
- Math class는 JVM이 설치된 OS의 메서드를 호출하여 사용
➠ OS에 의존적이므로 컴퓨터마다 결과가 다를 수 있다.
➠ 항상 같은 결과를 얻도록 성능을 다소 포기하여 작성한 class가 StrictMath class !
🔹 Math class의 메서드
메서드 | 기능 |
static double abs(double a) static float abs(float f) static int abs(int f) static long abs(long f) |
주어진 값의 절대값을 반환 |
static double ceil(double a) | 주어진 값을 올림하여 반환 |
static double floor(double a) | 주어진 값을 버림하여 반환 |
static double max(double a, double b) static float max(float a, float b) static int max(int a, int b) static long max(long a, long b) |
주어진 두 값을 비교하여 큰 쪽을 반환 |
static double min(double a, double b) static float min(float a, float b) static int min(int a, int b) static long min(long a, long b) |
주어진 두 값을 비교하여 작은 쪽을 반환 |
static double random() | 0.0~1.0 범위의 임의의 double값을 반환 (1.0은 범위에 포함 X) |
static double rint(double a) | 주어진 double값과 가장 가까운 정수값을 double형으로 반환 단, 두 정수의 가운데 있는 값(x.5)은 짝수를 반환 |
static long round(double a) static long round(float a) |
소수점 첫째자리에서 반올림한 정수값(long)을 반환 매개변수의 값이 음수인 경우, round()와 rint()읠 결과가 달라질 수 있다는 것에 주의 |
✔️ Wrapper class
🔸 객체지향 개념에서 모든 것은 객체로 다뤄져야 한다
- java가 완전한 객체지향 언어가 아닌 이유
➠ 8개의 기본형(primitive type)을 객체로 다루지 X ⭢ 대신 보다 높은 성능 ! - 기본형 변수를 객체로 다뤄야 하는 경우
ex) 매개변수로 객체를 요구, 기본형 값이 아닌 객체로 저장, 객체간의 비교가 필요할 때, etc.
➠ 기본형 값들을 객체로 변환하기 위해 사용되는 class가 Wrapper class !
Primitive type | Wrapper class | 생성자 |
boolean | Boolean | Booelan(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) |
🔻 Wrapper class의 생성자는 매개변수로 문자열(String)이나 각 자료형의 값들을 인자로 받는데,
생성자의 매개변수로 문자열을 제공할 때, 각 자료형에 맞는 문자열을 사용해야 한다!
ex) new Integer("1.0"); ➠ NumberFormatException 발생
- Overriding
- equals() ➠객체가 갖고 있는 값 비교
- toString ➠ 객체가 갖고 있는 값을 문자열로 변환하여 반환
- Auto-boxing이 된다고 해도 Integer class의 객체에 비교연산자 사용 불가능
➠ compareTo() 사용
🔹 Number class

- Number class
- Wrapper class type ➞ Primitive type
➠ 객체가 갖고 있는 값을 숫자와 관련된 기본형으로 변환하여 반환하는 메서드- intValue()
- longValue()
- floatValue()
- doubleValue()
- Wrapper class type ➞ Primitive type
- BigInteger class
- long으로도 다룰 수 없는 큰 범위의 정수를 처리하기 위한 class
- BigDecimal class
- double로도 다룰 수 없는 큰 범위의 부동 소수점수를 처리하기 위한 class
🔹 문자열 ➙ 숫자
int i = new Integer("100").intValue(); // floatValue(), longValue(), ...
int i2 = Integer.parseInt("100"); // Double.parseDouble("100"), ...
Integer i3 = Integer.valueOf("100"); // Long.valueOf("100"), ...
🔻 '타입.parse타입(String s)' 메서드 ➠ 반환값이 기본형(Primitive type)
'타입.valueOf()' 메서드 ➠ 반환값이 래퍼 클래스 타입(Wrapper class type)
int i3 = Integer.valueOf("100");
🔻 ' Autoboxing ' 기능( JDK1.5 ~ ) 때문에 반환값이 기본형일 때와 래퍼 클래스일 때의 차이가 사라졌다 !
➠ 성능은 조금 더 느리긴 하지만, 구별없이 valueOf()를 쓰는 것도 괜찮다.
static int parseInt(String s, int radix)
static Integer valueOf(String s, int radix)
int i = Integer.parseInt("100", 2); // 100(2) -> 4
🔻문자열이 10진수가 아닌 다른 진법(radix)의 숫자일 때도 변환이 가능하도록 하는 메서드
🔸 Autoboxing & Unboxing
- Autoboxing
- 기본형 값을 Wrapper class의 객체로 자동 변환시켜주는 것
int i = 1;
Integer iObj = new Integer(7);
int sum = i + iObj; // Error : 기본형과 참조형 간의 덧셈 불가 (JDK1.5 이전)
// Compile 후의 코드
int i = 1;
Integer iObj = new Integer(7);
int sum = i + iObj.intValue(); // 자동 형변환
- Unboxing (↔ Autoboxing)
- Wrapper class의 객체를 기본형 값으로 자동 변환 시켜주는 것
🔻 ArrayList에 숫자를 저장하거나 꺼낼 때 편리 !
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(10); // Autoboxing : 10 → new Integer(10)
int value = list.get(0); // Unboxing : new Integer(10) → 10
🔺 Autoboxing & Unboxing은 Compiler가 제공하는 편리한 기능일 뿐
기본형과 참조형 간의 형변환 · 연산이 불가능하다는 java의 원칙이 바뀐 것 X
➠ 생성자가 없는 class에 Compiler가 자동적으로 기본 생성자를 추가해 주듯이
개발자가 간략하게 쓴 구문을 Compiler가 원래의 구문으로 변경해주는 것 !
int i = 1;
Integer intg = (Integer) i;
Object obj = (Object) i;
Long lng = 100L;
// Compile 후
Integer intg = Integer.valueOf(i);
Object obj = (Object)Integer.valueOf(i);
Long lng = new Long(100L);
🌒 유용한 클래스
✔️ java.util.Objects class
🔹 Object의 보조 class
- Math class처럼 모든 메서드가 ' static '
static boolean isNull() // ⭤ nonNull()
🔻 해당 객체가 null이면 true 반환, 아니면 false
- requireNonNull()
static T requireNonNull(T obj)
static T requireNonNull(T obj, String message)
static T requireNonNull(T obj, Supplier messageSupplier)
🔻 해당 객체가 null인 경우, NullPointerException을 발생시킨다.
🔻 두 번째 매개변수로 지정하는 문자열(message)은 예외의 메시지가 된다.
➠ 매개변수의 유효성 검사 !
- compare()
static int compare(Object a, Object b, Comparator c)
🔻 두 비교대상이 같으면 0, 크면 양수, 작으면 음수를 반환한다.
- equals()
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
🔻 내부에서 a와 b의 null 검사를 하기 때문에,
Object class의 ' a.equals(b) '와 달리 null 검사를 위한 조건식을 따로 넣지 않아도 된다.
🔻 a와 b가 모두 null인 경우에는 참을 반환
- deepEquals()
static boolean deepEquals(Object a, Object b)
🔻 객체를 재귀적으로 비교하기 때문에, 다차원 배열의 비교도 가능 !
String[][] str2D = new String[][] {{"aaa", "bbb"}, {"AAA", "BBB"}};
String[][] str2D2 = new String[][] {{"aaa", "bbb"}, {"AAA", "BBB"}};
System.out.println(Objects.equals(str2D, str2D2));
System.out.println(Objects.deepEquals(str2D,str2D2));
false
true
➔ equals()로 2차원 이상의 문자열 배열 비교할 때엔 반복문을 함께 써야하지만,
deepEquals()는 반복문 없이 가능 !
- toString
static String toString(Obect o)
static String toString(Obect o, String nullDefault)
🔻 Object class의 ' toString() ' 메서드와 달리 내부적으로 null 검사 진행
🔻 ' o '가 null일 때, 두 번째 매개변수를 대신 사용할 값으로 지정 가능
- hashCode()
static int hashCode(Object o)
static int hashCode(Object... values)
🔻 내부적으로 null 검사 진행하고 Object class의 ' hashCode() ' 메서드를 호출
null일 때엔 0을 반환 !
🔻 보통 class에 선언된 instance의 변수들의 hashCode()를 조합해서 반환하도록 Overriding하지만,
그 대신 hashCode()매개변수의 타입이 가변인자 인 두 번째 메서드로 대체 가능 !
🔸 Objects class를 static import 하더라도 Object class의 메서드와
같은 이름의 메서드를 사용하면 Compiler가 구별하지 못해 충돌 발생 !
import static java.util.Objects.*;
...
// equals(str2D, str2D_2_; // Error !
Objects.equals(str2D, str2D_2_; // OK !
➔ 이러한 경우엔 class의 이름을 붙여줄 수밖에 없다 !
✔️ java.util.Random class
🔹 Math.random() & Random.next기본형타입(기본형타입 n)
- Math.random()도 내부적으로 Random class의 instance를 생성해서 사용하는 것으로 차이 X
int num = (int)(Math.random() * 6) + 1;
int num = new Random().nextInt(6) + 1; // nextInt(6) : 0 ~ 5 사이의 정수를 반환
- 종자값이 같은 Random class의instance들은 항상 같은 난수를 같은 순서대로 반환하기 때문에,
종자값(seed)를 설정할 수 있다는 점에서 차이가 있다 ! - ' nextInt(int n) ' 일 때, n은 범위에 포함 X
🔹 Random class의 생성자 & 메서드
Random() // 현재시간(System.currentTimeMillis())을
// 종자값(seed)으로 이용하는 Random class의 instance 생성
Rnadom(long seed) // 매개변수 seed를 종자값으로 하는 Random class의 instance 생성
void setSeed(long seed) // 종자값을 주어진 값(seed)로 변경
- 생성자 Random()은 위처럼 현재시간을 1/1000 단위로 변환하여 반환한 값을
종자값으로 설정하기 때문에 실행할 때마다 얻는 난수가 달라진다. - nextBytes()
➠ BigInteger(int signum, byte[] magnitude)와 함께 사용하면
int의 범위보다 더 넓은 범위의 난수를 얻을 수 있다.
🔸 Math.random()을 이용해서 만든 실제 프로그래밍에 유용할만한 메서드
- int[ ] fillRand(int[] arr, int from, int to)
: 배열 arr을 from과 to 범위의 값들로 채워서 반환 - int[ ] fillRand(int[ ] arr, int[ ] data)
: 배열 arr을 배열 data에 있는 값들로 채워서 반환 - int getRand(int from, int to)
: from과 to 범위의 정수(int)값을 반환하며, from과 to 모두 범위에 포함 !
public static int[] fillRand(int[] arr, int from, int to) {
for(int i = 0; i < arr.length; i++)
arr[i] = getRand(from, to);
return arr;
}
public static int[] fillRand(int[] arr, int[] data) {
for(int i = 0; i < arr.length; i++)
arr[i] = data[getRand(0, data.length -1)];
return arr;
}
public static int getRand(int from, int to) {
return (int) (Math.random() * (Math.abs(to-from) + 1))
+ Math.min(from, to); // from 값 반환
// abs : 절대값으로 반환하는 메서드
}
🔻 불연속적인 범위의 있는 값을 저장해서 임의로 얻어와야 하는 경우에
위의 메서드들이 유용하게 쓰인다.
ex ) Database에 넣은 테스트 데이터를 만드는 경우
✔️ 정규식 ( Regular Expression ) - java.util.regex package
🔹 text 데이터 중에서 원하는 pattern과 일치하는 문자열을 찾아내기 위해 사용하는 것
🔹 package ' regex '
- Pattern class
➠ 정규식을 정의하는 역할
Pattern p = Pattern.compile("c[a-z]*");
🔻 Pattern class의 정규식을 매개변수로 하는 static 메서드
Pattern compile(String regex) '를 호출하여 Pattern class의 instance를 생성
🔻 Pattern class 메서드
boolean matches(String regex, CharSequence input) // 정규표현식의 패턴과 일치하는지 체크
Pattern asPredicate() // 문자열을 일치시키는데 사용할 수 있는 술어를 작성
String pattern() // 컴파일된 정규표현식을 String 형태로 반환
Pattern split(CharSequence input) // 문자열을 주어진 인자값 CharSequence 패턴에 따라 분리
- Matcher class
➠ 정규식(pattern)을 데이터와 비교하는 역할 ( group() 메서드를 이용하면 출력도 가능 )
Matcher m = p.matcher(data[i]);
if(m.matches()) { ... }
🔻 Pattern class의 정규식으로 비교할 대상을 매개변수로 하는 메서드
' Matcher matcher(CharSequence input) '를 호출해서 Matcher class의 instance를 생성
➠ ' CharSequence ' 은 interface 타입이며, 다형성 기능을 통해
문자열 말고도 다양한 형태의 입력 데이터를 받을 수 있다.
( 이를 구현한 class는 CharBuffer, String, StringBuffer 가 있다. )
🔻 이렇게 생성된 instance로 ' boolean matches() '를 호출해서 정규식에 부합하는지 확인
🔻 Matcher class 메서드
find() // 패턴이 일치하는 경우 true, 불일치하는 경우 false 반환
// 반복 호출하는 경우, 이전에 발견한 부분의 다음부터 다시 패턴 매칭 시작
find(int start) // start 위치 이후부터 매칭검색
start() // 매칭되는 문자열의 시작위치 반환
start(int group) // 매개변수로 받은 group이 매칭되는 시작위치 반환
end() // 매칭되는 문자열의 끝위치의 다음 문자위치 반환
end(int group) // 매개변수로 받은 group이 매칭되는 끝위치의 다음 문자위치 반환
group() // group(0) : 그룹으로 매칭된 문자열을 전체를 나누어지지 않은 채로 반환
group(int group) // 그룹화되어 매칭된 패턴 중 group 번째 부분 반환
groupCount() // 괄호로 지정해서 그룹화한 패턴의 전체 개수 반환
matches() // 패턴이 전체 문자열과 일치할 경우 true 반환
// (일부 문자열이 아닌 전체 문자열과 완벽히 일치)
appendReplacement(StringBuffer sb, String replacement) // 패턴과 일치하는 문자열을 'replacement'로 대체하는 메서드
appendTail(StringBuffer sb) // 마지막으로 치환된 이후의 부분을 sb에 덧붙이는 메서드
➠ 그룹화 ( grouping ) : 정규식의 일부를 괄호로 나누어 묶을 수 있다.
➠ ' group(int i) '를 호출할 때 i가 실제 group의 수보다 많으면 예외 발생
( java.lang.IndexOutOfBoundsException )
🔹 String class의 정규식 메서드
➠ String 문자열에 바로 정규표현식을 적용하여 필터링 가능 !
- String.matches()
boolean matches(String regex)
🔻 매개변수로 주어진 정규식 ' regex '에 매칭되는 값이 있는지 확인
- String.replaceAll()
String replaceAll(String regex, String replacement)
🔻 문자열내에서 정규식 ' regex '과 매치되는 모든 문자열을
문자열 ' replacement '로 바꾼 문자열 반환
- String.split()
String[] split(String regex)
🔻 매개변수로 주어진 정규식 ' regex '과 매치되는 문자열을 구분자로 하여 분할
🔹 정규식 문법 기호 모음
- 정규식 기본 기호
기호 | 설명 |
. | 임의의 문자 1개를 의미 |
^ | 시작을 의미 ⭢ ^a : a로 시작하는 단어 단, [ ] 괄호 안에 있다면 일치하지 않다는 부정의 의미 ⭢ [^a-z] : a부터 z까지를 제외한 모든 문자 |
$ | $ 앞의 문자열로 문자가 끝나는 단어 |
[ ] | [ ] 괄호 안의 문자가 있는지 확인 ⭢ [ab][cd] : a,b 중 한 문자와 c,d 중 한 문 |
- | 사이의 문자 혹은 숫자를 의미 ⭢ [a-z0-9] : 알파벳 소문자 전체, 0-9 중 한 문자 |
| | ' 또는 ' 을 의미 ⭢ [a|b] : a 또는 b |
( ) | 그룹(group)을 의미 ⭢ 01(0|1) : "01" 뒤에 "0" 또는 "1"이 들어다 |
{ } | 개수를 의미 ⭢ a{3}b : "a"가 3번 온 후 "b"가 온다 |
\b | 공백, tap, ",", "/" 등을 의미 ⭢ dog\b : dog cat (o) , dog/ (o) , dog. (x) |
\B | \b 와 반대의 의미 공백, tap, ",", "/" 등이 아닌 문자인 경우 매치 |
\d | 0~9 사이의 숫자 ( = ' [0-9] ' ) |
\D | \d 와 반대의 의미 숫자가 아닌 어떤 문자 ⭢ [^0-9] |
\s | 공백, 탭 |
\S | 공백, 탭이 아닌 문자 |
\w | 알파벳 대소문자, 숫자, "_" ⭢ [a-zA-Z_0-9] |
\W | \w 와 반대의 의미 ⭢ [^a-zA-z_0-9] |
- 정규식 수량 기호
기호 | 설명 |
? | 앞의 표현식이 없거나 최대 한 개만 있을 수 있다는 의미 |
* | 앞의 표현식이 있을 수도 없을 수도 있다는 의미 |
+ | 앞의 표현식이 1개 이상 있다는 의미 |
{n} | 앞의 표현식이 딱 n개 있다는 의미 |
{n,m} | 앞의 표현식이 n개 이상 m 개 이하로 있다는 의미 |
{n,} | 앞의 표현식이 n개 이상 있다는 의미 |
- 자주 사용되는 정규식 샘플
^[0-9]*$ // 숫자
^[a-zA-Z]*$ // 영문자
^[가-힣]*$ // 한글
\\w+@\\w+\\.\\w+(\\.\\w+)? // E-Mail
^\d{2,3}-\d{3,4}-\d{4}$ // 전화번호
^01(?:0|1|[6-9])-(?:\d{3}|\d{4})-\d{4}$ // 휴대전화번호
\d{6}\-[1-4]\d{6} // 주민등록번호
^d{3}-\d{2}$ // 우편번호
✔️ java.util.Scanner class
🔹Scanner class
- 다양한 입력 소스로부터 데이터를 읽어오는 목적으로 정의된 class
Scanner(String source)
Scanner(File source)
Scanner(InputStream source)
Scanner(Readable source)
Scanner(ReadableByteChannel source)
Scanner(Path source) // JDK.17 ~
Scanner useDelimiter(Pattern pattern)
Scanner usdDelimiter(String pattern)
🔻 위와 같은 생성자를 통해 화면, 파일, 문자열과 같은 입력소스로부터 문자데이터를 읽어올 수 있다.
Scanner sc = new Scanner(new File("data.txt");
int cnt = 0;
int totalSum = 0;
while(sc.hasNextLine()) {
String line = sc.nextLine();
Scanner sc2 = new Scanner(line).useDelimiter(",");
int sum = 0;
while(sc2.hasNextInt()) {
sum += sc2.nextInt();
}
System.out.println(line + ", sum = " + sum);
totalSum += sum;
cnt++;
}
System.out.println("Line: " + cnt + ", Total: " + totalSum);
🔻 정규식 표현을 이용한 라인단위의 검색을 지원하며
구분자에도 정규식 표현을 사용할 수 있어서 복잡한 형태의 구분자도 처리 가능
🔹 Scanner class의 메서드
boolean nextBoolean()
byte nextByte()
short nextShort()
int nextInt()
long nextLong()
double nextDouble()
float nextFloat()
String nextLine()
🔻 이와 같은 메서드 덕분에 입력받은 문자를 다시 변환하는 작업 X
➠ 입력된 데이터의 형식에 맞는 메서드를 사용하지 않으면 inputMismatchException 발생
✔️ java.util.StringTokenizer class
🔹 StringTokenizer class
- 긴 문자열을 지정된 구분자를 기준으로 토큰(token)이라는 여러 개의 문자열로 잘라내는 데 사용
- String split(String regex)이나 Scanner useDelimiter(String pattern)을 사용해도 되지만,
StringTokenizer는 정규식 표현을 사용하지 않고 사용 가능 !
🔹 StringTokenizer의 생성자 & 메서드
- StringTokenizer()
StringTokenizer(String str, Strin delim) // 구분자 ≠ token
StringTokenizer(String str, String delim, boolean returnDelims)
// returnDelims = true 인 경우, 구분자 = token
- countToken()
int countToken()
🔻 전체 토큰(token)의 수 반환
- hasMoreTokens()
boolean hasMoreTokens()
🔻 토큰(token)이 남아있는지를 체크하고 결과를 반환
- nextToken()
String nextToken()
🔻 다음 토큰(token)을 반환
✔️ java.math.BigInteger class
🔹 BigInteger class
- long(정수를 표현할 수 있는 가장 큰 타입)으로 표현할 수 있는 범위(약 19자리)를 넘어서면
모두 0으로 출력되는 상황 발생
➠ BigInteger는 저장할 수 있는 숫자의 범위가 무한하다!
➠ 내부적으로 int 배열을 사용해서 값을 다루기 때문이다. ( 성능은 long 타입보다 떨어진다. )
final int signum; // 부호 : 1(양수), 0, -1(음수)
final int[] mag; // 값(magnitude)
🔻 String처럼 BigDecimal도 불변(immutable)이며, 값을 '2의 보수' 형태로 표현
🔹 BigInteger class의 생성자
BigInteger val;
val = new BigInteger("1234567890"); // 문자열로 생성
val = new BigInteger("FFFF", 16); // n진수(radix)의 문자열로 생성
val = BigInteger.valueOf(123456890L); // 숫자로 생성
🔻 생성자를 호출할 때, 인자 값으로 문자열을 넘겨주어야 한다.
🔻 n진수(radix)의 문자열로도 생성 가능
🔻 valueOf 메서드(static)를 이용하면 숫자로 초기화 가능
🔹 BigInteger class의 메서드 ( 연산 )
BigInteger add(BigInteger val) // 덧셈 (this + val)
BigInteger subtract(BigInteger val) // 뺄셈 (this - val)
BigInteger multiply(BigInteger val) // 곱셍 (this * val)
BigInteger divide(BigInteger val) // 나눗셈 (this / val)
BigInteger remainder(BigInteger val) // 나머지 (this % val)
🔻 BigInteger는 불변이므로, 반환타입이 BigInteger ➔ 새로운 instance가 반환된다는 의미 !
🔹 비트 연산 메서드
int bitCount() // 2진수로 표현했을 때, 1의 개수(음수는 0의 개수)를 반환
int bitLength() // 2진수로 표현했을 때, 값을 표현하는데 필요한 bit 수
boolean testBit(int n) // 우측에서 n+1번째 비트가 1이면 true, 0이면 false
BigInteger setBit(int n) // 우측에서 n+1번째 비트를 1로 변경
BigInteger clearBit(int n) // 우측에서 n+1번째 비트를 0으로 변경
BigInteger flipBit(int n) // 우측에서 n+1번째 비트를 번환 (1 → 0, 0 → 1)
🔻 큰 숫자를 다루기 위한 class인 만큼 성능을 향상시키기 위해 비트 단위로도 연산 수행
BigInteger bi = new BigInteger("4");
if(bi.remainder(new BigInteger("2")).equals(BigInteger.ZERO)) {
...
BigInteger bi = new BigInteger("4");
if(!bi.testBit(0)) { // 제일 오른쪽 비트가 0인지 확인
...
🔻 비트 연산 메서드를 사용하면 더 효율적인 코드 작성 가능
✔️ java.math.BigDecimal class
🔹 BigDecimal class
- double(소수점을 표현할 수 있는 가장 큰 타입)은 소수점의 정밀도에 있어서 한계(최대 13자리)가
존재하여 값이 유실되는 상황 발생 ( double은 내부적으로 수를 저장할 때 2진수의 근사치를 저장 )
➠ BigDecimal은 속도가 느리긴 해도 수를 10진수 형태로 저장하여 거의 무한한 정밀도 보장
private final BigInteger intVal; // 정수 (unscaled value)
private final int scale; // 지수 (scale)
private transient int precision; // 정밀도 (precision) - 정수의 자릿수
🔻BigInteger처럼 BigDecimal도 불변(immutable) !
🔹 BigDecimal class의 생성자
BigDecimal val;
val = new BigDecimal("123.4567890"); // 문자열로 생성
val = new BigDecimal(123.456); // double 타입의 리터럴로 생성
val = new BigDecimal(123456); // int, long 타입의 리터럴로 생성 가능
val = BigDecimal.valueOf(123.456); // 생성자 대신 valueOf(double) 사용
val = BigDecimal.valueOf(123456); // 생성자 대신 valueOf(int) 사용
🔻 생성자를 호출할 때, 보통 인자 값으로 문자열을 넘겨준다.
➠ double 타입의 값을 인자 값으로 넘겨주면 오차 발생
🔻 valueOf 메서드(static)를 이용하면 숫자로 초기화 가능
🔹 다른 타입으로의 변환
String toPlainString() // 어떤 경우에도 다른 기호없이 숫자로만 표현
String toString() // 필요하면 지수형태로 표현 가능
🔻 BigDecimal을 문자열로 변환하는 메서드
🔻 '1.0e-22'와 같은 지수 형태의 리터럴을 사용한 경우 다른 문자열을 반환할 수도 있다.
int intValue()
long longValue()
float floatValue()
double doubleValue()
🔻 Number로부터 상속받은 기본형으로 변환하는 메서드
byte byteValueExact()
short shortValueExact()
int intValueExact()
long longValueExact()
BigInteger toBigInterExact()
🔻정수형으로 변환하는 메서드 중에서 이름 끝에 ' Exact '가 붙은 메서드들은
변환환 결과가 변환한 타입의 범위에 속하지 않으면 예외(ArithmeticException)을 발생시킨다.
🔹 BigDecimal의 연산
BigDecimal add(BigDecimal val) // 덧셈 (this + val)
BigDecimal subtract(BigDecimal val) // 뺄셈 (this - val)
BigDecimal multiply(BigDecimal val) // 곱셍 (this * val)
BigDecimal divide(BigDecimal val) // 나눗셈 (this / val)
BigDecimal remainder(BigDecimal val) // 나머지 (this % val)
🔻 BigDecimal도 불변이므로, 반환타입이 BigDecimal ➔ 새로운 instance가 반환된다는 의미 !
🔻 단, 연산결과의 정수, 지수, 정밀도는 달라질 수 있다 !
🔹 반올림 모드 - divide( ) & setScale( )
BigDecimal divide(BigDecimal divisor)
BigDecimal divide(BigDecimal divisor, int rouningMode)
BigDecimal divide(BigDecimal divisor, RoundingMode rouningMode)
BigDecimal divide(BigDecimal divisor, int scale, int rouningMode)
BigDecimal divide(BigDecimal divisor, int scale, RoundingMode rouningMode)
BigDecimal divide(BigDecimal divisor, MathContext mc)
🔻 열거형 RoundingMode를 사용하여 BigDecimal의 나눗셈 결과를
반올림(roundingMode) 처리해준다 !
열거형 RoundingMode에 정의된 상수 |
설명 |
CEILING | 올림 |
FLOOR | 내림 |
UP | 양수일 때는 올림, 음수일 때는 내림 |
DOWN | 양수일 때는 내림, 음수일 때는 올림 |
HALF_UP | 일반적인 반올림 |
HALF_EVEN | 반올림 자리의 값이 짝수면 HALF_DOWN, 홀수면 HALF_UP |
HALF_DOWN | 6이상 올림, 6미만 버림 |
UNNECESSARY | 나눗셈의 결과가 딱 떨어지는 수 X ➔ ArithmeticException |
BigDecimal bigd = new BigDecimal("1.0");
BigDecimal bigd2 = new BigDecimal("3.0");
System.out.println(bigd.divide(bigd2)); // ArithmeticException 발생
System.out.println(bigd.divide(bigd2, 3, RoundingMode.HALF_UP)); // 0.333
🔻 divide()의 결과가 무한소수인 경우, RoundingMode를 지정해주지 않으면 예외 발생
🔹 java.math.MathContext
- RoundingMode와 precision(정밀도)를 하나로 묶어 놓은 class
- recision ➠ 정수와 소수점 이하를 모두 포함한 자리수를 의미
BigDecimal bigd = new BigDecimal("123.456");
BigDecimal bigd2 = new BigDecimal("1.0");
System.out.println(bigd.divide(bigd2, 2, RoundingMode.HALF_UP)); // 123.45
System.out.println(bigd.divide(bigd2, new MathContext(2, HALF_UP)); // 1.2E+2
🔹 scale의 변경
BigDecimal setScale(int newScale)
BigDecimal setScale(int newScale, int roundingMode)
BigDecimal setScale(int newScale, RoundingMode mode)
🔻 setScale()로 scale 값 줄이는 것 = 10의 n제곱으로 나누는 것
🔻 이때도 RoundingMode를 지정해주어야 한다!