Java의 정석 의 책을 읽고 정리한 내용입니다.
- java.lang패키지의 클래스들은 import문 없이도 사용할 수 있다.
Object클래스의 메서드 | 설명 |
---|---|
protected Object clone() | 객체 자신의 복사본을 반환한다. |
public boolean equals(Object obj) | 객체 자신과 객체 obj가 같은 객체인지 알려준다. (같으면 true) |
protected void finalize() | 객체가 소멸될 때 가비지 컬렉터에 의해 자동적으로 호출된다. |
이 때 수행되어야하는 코드가 있을 때 오버라이딩한다. (거의 사용 안함) | |
public Class getClass() | 객체 자신의 클래스 정보를 담고 있는 Class인스턴스를 반환한다. |
public int hashCode() | 객체 자신의 해시코드를 반환한다. |
public String toString() | 객체 자신의 정보를 문자열로 반환한다. |
public void notify() | 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다. |
public void notifyAll() | 객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨운다. |
public void wait(), public void wait(long timeout), public void wait(long timeout, int nanos) | 다른 쓰레드가 notify()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게 한다. (timeout은 천 분의 1초, nanos는 10의 9승 분의 1초) |
✔ equals(Object obj)
boolean
값으로 알려준다.
class Person{
long id;
public boolean equals(Object obj){
if(obj instanceof Person){
return id == ((Person)obj).id; // obj가 Object 타입이므로 id 값을 참조하기 위해서는
// Person 타입으로 형 변환이 필요하다.
}else{
return false; // 타입이 Person이 아니면 값을 비교할 필요도 없다.
}
}
Person(long id){
this.id = id;
}
}
public class EqualsEx2 {
public static void main(String[] args){
Person p1 = new Person(80118111122L);
Person p2 = new Person(80118111122L);
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
메서드를 오버라이딩했다.
같은 내용의 문자열을 갖는 두 String인스턴스에 equals메서드를 사용하면 항상 true값을 얻는 것이다.
✔ hashCode()
- 이 메서드는 해싱(hashing) 기법에 사용되는 '해시함수(hash function)'를 구현한 것이다.
- 해싱은 데이터관리기법 중의 하나인데 다량의 데이터를 저장하고 검색하는 데 유용하다.
- 해시함수는 찾고자하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드(hashcode)를 반환한다.
💡 참고
- 해싱기법을 사용하는 HashMap이나 HashSet과 같은 클래스에 저장할 객체라면 반드시 hashCode()메서드를 오버라이딩해야 한다.
public class HashCodeEx1 {
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
hashCode
메서드가 오버라이딩되어 있기 때문에, 문자열의 내용이 같은 str1과 str2에 대해 hashCode()
를 호출하면 항상 동일한 해시코드 값을 얻는다.
✔ toString()
- 이 메서드는 인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것이다. 인스턴스의 정보를 제공한다는 것은 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 뜻이다.
- 클래스를 작성할 때
toString()
을 오버라이딩하지 않는다면,toString()
을 호출하면 클래스 이름에 16진수의 해시코드를 얻게 될 것이다.
String
클래스의 toString()
은 String
인스턴스가 갖고 있는 문자열을 반환하도록 오버라이딩되어 있고, Date
클래스의 경우 Date
인스턴스가 갖고 있는 날짜와 시간을 문자열로 변환하여 반환하도록 오버라이딩되어 있다.
이처럼 toString()
은 일반적으로 인스턴스나 클래스에 대한 정보 또는 인스턴스 변수들의 값을 문자열로 변환하여 반환하도록 오버라이딩되는 것이 보통이다.
Object
클래스에 정의된 toString()
의 접근제어자가 public
이므로 Card
클래스의 toString()
의 접근제어자도 public
으로 했다는 것을 눈여겨 보자.
✔ clone()
clone()
을 호출할 수 있다. 이 인터페이스를 구현하지 않고 clone()
을 호출하면 예외가 발생한다.clone()
을 사용하려면, 먼저 복제할 클래스가 Cloneable인터페이스를 구현해야하고, clone()
을 오버라이딩하면서 접근 제어자를 protected
에서 public
으로 변경한다. 그래야만 상속관계가 없는 다른 클래스에서 clone()
을 호출할 수 있다.Cloneable
인터페이스가 구현되어 있다는 것은 클래스 작성자가 복제를 허용한다는 의미이다.
✔ 공변 반환타입
int [] arr = {1,2,3,4,5};
int[] arrClone = arr.clone();
➡
int[] arr = {1,2,3,4,5};
int[] arrClone= new int[arr.length]; // 배열 생성
System.arraycopty(arr,0,arrClone,0,arr.length); // 내용 복사
클래스들 복사
ArrayList list = new ArrayList();
...
ArrayList list2 = (ArrayList)list.clone();
✔ 얕은 복사와 깊은 복사
- 얕은 복사(shallow copy)의
clone()
은 단순히 객체에 저장된 값을 그대로 복제할 뿐, 객체가 참조하고 있는 객체까지 복제하지 않는다. 얕은 복사에서는 원본을 변경하면 복사본도 영향을 받는다.- 깊은 복사(deep copy) : 원본이 참조하고 있는 객체까지 복제하는 것, 깊은 복사에서는 원본과 복사본이 서로 다른 객체를 참조하기 때문에 원본의 변경이 복사본에 영향을 미치지 않는다.
ex) 얕은 복사 예시
class Circle implements Cloneables{
Point p; //원점 -참조변수
double r; //반지름
Circle(Point p, double r) {
this.p = p;
this.r = r;
}
public Circle clone() { //얕은 복사
Object obj = null;
try{
obj = super.clone(); //조상인 Object의 clone()을 호출한다.
}catch(CloneNotSupportedException e){ }
return (Circle)obj;
}
}
Circle인스턴스 c1을 생성하고 clone(0으로 복제해서 c2를 생성
Circle c1 = new Circle(new Point(1,1),2.0);
Circle c2 = c1.clone(); //얕은 복사
class Circle implements Cloneable{
Point3 p; //원점
double r; //반지름
Circle(Point3 p, double r){
this.p = p;
this.r = r;
}
public Circle shallowCopy(){ //얕은 복사
Object obj = null;
try{
obj = super.clone();
}catch(CloneNotSupportedException e){}
return (Circle)obj;
}
public Circle deepCopy(){ //깊은 복사
Object obj = null;
try{
obj = super.clone();
}catch(CloneNotSupportedException e){}
Circle c =(Circle)obj;
c.p =new Point3(this.p.x, this.p.y);
return c;
}
public String toString(){
return "[p="+p+", r="+r+"]";
}
}
class Point3{
int x,y;
Point3 (int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
return "("+x+", "+y+")";
}
}
public class ShallowDeepCopy {
public static void main(String[] args) {
Circle c1 =new Circle(new Point3(1,1),2.0);
Circle c2 = c1.shallowCopy();
Circle c3 = c1.deepCopy();
System.out.println("c1="+c1);
System.out.println("c2="+c2);
System.out.println("c3="+c3);
c1.p.x = 9;
c1.p.y = 9;
System.out.println("= c1의 변경 후 =");
System.out.println("c1="+c1);
System.out.println("c2="+c2);
System.out.println("c3="+c3);
}
}
c1=[p=(1, 1), r=2.0]
c2=[p=(1, 1), r=2.0]
c3=[p=(1, 1), r=2.0]
= c1의 변경 후 =
c1=[p=(9, 9), r=2.0]
c2=[p=(9, 9), r=2.0]
c3=[p=(1, 1), r=2.0]
Circle c1 = new Circle(new Point(1,1), 2.0);
Circle c2 = c1.shallowCopy();
Circle c3 = c1.deepCopy();
deepCopy()
는 복제된 객체가 새로운 Point인스턴스를 참조하도록 하였다. 원본이 참조하고 있는 객체까지 복사한 것이다.
✔ getClass
- 이 메서드는 자신이 속한 클래스의 Class객체를 반환하는 메서드
- Class객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재한다.
- 클래스 파일을 읽어서 사용하기 편한 형태로 저장해 놓은 것이 클래스 객체이다.
- 클래스 파일을 메모리에 로드하고 변환하는 일은 클래스 로더가 한다.
Class 객체에 대한 참조를 얻는 방법
Class cObj = new Card().getClass(); //생성된 객체로부터 얻는 방법
Class cObj = Card.class; //클래스 리터럴(*.calss)로 부터 얻는 방법
Class cObj = Class.forNmae("Card"); //클래스 이름으로부터 얻는 방법
Card c = new Card(); //new 연산자를 이용해서 객체를 생성
Card c = Card.class.newInstance(); //Class객체를 이용해서 객체를 생성
String클래스는 문자열을 저장하고 이를 다루는데 필요한 메서드를 함께 제공한다.
✔ 변경 불가능한(immutable) 클래스
+
연산자를 이용해서 문자열을 결합하는 경우 인스턴스내의 문자열이 바뀌는 것이 아니라 새로운 문자열("ab")이 담긴 String인스턴스가 생성되는 것이다.String a = "a";
String b = "b";
String a = a + b; //새로운 문자열 참조
✔ 문자열의 비교
문자열 리터럴을 지정하는 방법, String클래스의 생성자를 사용해서 만드는 방법이 있다.
String str1 = "abc"; //문자열 리터럴 "abc"의 주소가 str1에 저장됨
String str2 = "abc"; //문자열 리터럴 "abc"의 주소가 str2에 저장됨
String str3 = new String("abc"); //새로운 String인스턴스를 생성
String str4 = new String("abc"); //새로운 String 인스턴스를 생성
✔ 문자열 리터럴
- 자바소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다. 이 때 같은 내용의 문자열 리터럴은 한번만 저장된다.
- String리터럴들은 컴파일시에 클래스 파일에 저장된다.
✔ 빈 문자열(empty string)
- 길이가 0인 배열이 존재할 수 있다.
char[] chArr = new char[0]; // 길이가 0인 char배열
int [] iArr ={}; // 길이가 0인 int 배열
String s = null; ➡ "" 빈 문자열로 초기화
String c ='\u0000'; ➡ ' ' 공백으로 초기화
✔ String클래스의 생성자와 메서드
메서드 | 설명 | 예제 | 결과 |
---|---|---|---|
String(String a) | 주어진 문자열(s)를 갖는 Stirng 인스턴스를 생성한다. | String s = new String("Hello"); | s ="Hello" |
String(char[] value) | 주어진 문자열(value)를 갖는 String 인스턴스를 생성한다. | char[ ] c = {'H','e','l','l','o'}; String s =new String(c); | s= "Hello" |
String(StringBuffer buf) | StringBuffer인스턴스가 갖고 있는 문자열과 같은 내용의 String인스턴스를 생성한다. | StringBuffer sb = new StringBuffer("Hello"); String s = new String(sb); | s = "Hello" |
char charAt(int index) | 지정된 위치(index)에 있는 문자를 알려준다. (index는 0부터 시작) | String s = "Hello";String n = "0123456";char c = s.charAt(1);char c2 = n.charAt(1); | c = 'e', c2 ='1' |
int compareTo(string str) | 문자열(str)과 사전순서로 비교한다. 같으면 0을 , 사전순으로 이전이면 음수를, 이후면 양수를 반환한다. | int i = "aaa".compareTo("aaa"); int i2 = "aaa".compareTo("bbb"); int i3 = "bbb".compareTo("aaa") | i = 0, i2 = -1, i3 =1 |
String concat(String str) | 문자열(str)을 뒤에 덧붙인다. | String s ="Hello"; String s2 = s.concat(" World"); | s2 ="Hello World" |
boolean contains(CharSequence s) | 지정된 문자열(s)가 포함되어 있는지 검사한다. | String s ="abcdefg"; boolean b = s.concat("bc"); | |
boolean endsWith(String suffix) | 지정된 문자열 (suffix)로 끝나지는 검사한다. | String file = "Hello.text"; boolean b = file.endWith("txt"); | b =true |
boolean equals(Object obj) | 매개변수로 받은 문자열(obj)과 String인스턴스의 문자열을 비교한다. obj가 String이 아니거나 문자열이 다르면 false를 반환한다. | String s ="Hello"; boolean b = s.equals("Hello"); boolean b2 =s.equals("hello"); | b= true, b2 = false |
boolean equalsIgnoreCase(String str) | 문자열과 String인스턴스의 문자열을 대소문자 구분없이 비교한다. | String s ="Hello"; boolean b = s.equalsIgnoreCase("Hello"); | |
boolean b2 =s.equalsIgnoreCase("hello"); | b= true, b2 =true | ||
int indexOf(int ch) | 주어진 문자(ch)가 문자열에 존재하는지 확인하여 위치(index)를 알려준다. 못찾으면 -1을 반환한다(index는 0부터 시작) | String s = "Hello"; int idx1 = s.indexOf('o'); int idx2 = s.indexOf('k'); | idx1 =4, idx2 =-1 |
int indexOf(int ch, int pos) | 주어진 문자(ch)가 문자열에 존재하는지 지정된 위치(pos)부터 확인하여 위치(index)를 알려준다. 못찾으면 -1을 반환한다.(index는 0부터 시작) | String s = "Hello"; int idx1 = s.indexOf('e', 0); int idx2 = s.indexOf('e',2); | idx1 = 1, idx2 = -1 |
int indexOf(String str) | 주어진 문자열이 존재하는지 확인하여 그 위치(index)를 알려준다. 없으면 -1을 반환한다.(index는 0부터 시작) | String s = "ABCDEFG"; int idx = s.indexOf("CD"); | idx=2 |
String intern() | 문자열을 상수풀(constant pool)에 등록한다. 이미 상수풀에 같은 내용의 문자열이 있을 경우 그 문자열의 주소값을 반환한다. | String s = new String("abc"); String s2 =new String("abc");boolean b = (s == s2);boolean b2 =s.equals(s2); boolean b3 = (s.intern() == s2.intern() ); | b =false, b2 =true, b3 =true |
int lastIndexOf(int ch) | 지정된 문자 또는 문자코드를 문자열의 오른쪽 끝에서부터 찾아서 위치(index)를 알려준다. 못 찾으면 -1을 반환한다. | String s = "java.lang.Object"; int idx1 = s.lastIndexOf('.'); int idx2 = s.indexOf('.'); | idx1 = 9 idx2 = 4 |
int lastIndexOf(String str) | 지정된 문자열을 인스턴스의 문자열 끝에서부터 찾아서 위치(index)를 알려준다. 못찾으면 -1을 반환한다. | String s = "java.lang.java"; int idx1 = s.lastIndexOf("java"); int idx2 = s.indexOf("java"); | idx1 = 10 idx2 = 0 |
int length() | 문자열의 길이를 알려준다 | String s ="Hello" int length = s.length(); | length =5 |
String replace(char old, char nw) | 문자열 중의 문자(old)를 새로운 문자열(nw)로 모두 바꾼 문자열을 반환한다. | String s = "Hello"; String s1 = s.replace('H','C'); | s1 ="Cello" |
String replace(CharSequence old , CharSequence nw) | 문자열 중의 문자열(old)을 새로운 문자열(nw)로 모두 바꾼 문자열을 반환한다. | String s = "Hellollo"; String s1 = s.replace("ll", "LL"); | s1 = "HeLLoLLo" |
String replaceAll(String regex, String replacement) | 문자열 중 지정된 문자열 (regex)과 일치하는 것을 새로운 문자열(replacement)로 모두 변경한다. | String ab = "AABBAABB"; String r = ab.replaceAll("BB", "bb"); | r = "AAbbAAbb" |
String replaceFirst(String regex, String replacement) | 문자열 중에서 지정된 문자열(regex)과 일치하는 것 중, 첫번째 것만 새로운 문자열(replacement)로 변경한다. | String ab ="AABBAABB"; String r =ab.replaceFirst("BB", "bb"); | r ="AAbbAABB" |
String[] split(String regex) | 문자열을 지정된 분리자(regex)로 나누어 문자열 배열에 담아 반환한다. | String animals = "dog,cat,bear"; String[] arr =aniamls.split(","); | arr[0] = "dog" arr[1] ="cat" arr[2] ="bear" |
String[] split(String regex, int limit) | 문자열을 지정된 분리자(regex)로 나누어 문자열배열에 담아 반환한다. 단, 문자열 전체를 지정된 수(limit)로 자른다. | String animals = "dog,cat,bear"; String[] arr =animal.split(",", 2); | arr[0] = "dog" arr[1] ="cat,bear" |
boolean startsWith(String prefix) | 주어진 문자열(prefix)로 시작하는지 검사한다. | String s = "java.lang.Object"; boolean b = s.startWith("java"); boolean b2 =s.startsWith("lang"): | b =true |
b2 =false | |||
String substring(int begin) String substring(int begin, int end) | 주어진 시작위치(begin)부터 끝 위치(end)범위에 포함된 문자열을 얻는다. 이 떄 시작위치의 문자는 범위에 포함되지만, 끝 위치의 문자는 포함되지 않는다. (begin ≤ x <end) | String s ="java.lang.Object"; String c = s.substring(10); String p = s.substring(5, 9); | c ="Object" |
p ="lang" | |||
String toLowerCase() | String인스턴스에 저장되어있는 모든 문자열을 소문자로 변환하여 반환한다. | String s= "Hello"; String s1 = s.toLowerCase(); | s1 = "hello" |
String toString() | String인스턴스에 저장되어 있는 문자열을 반환한다. | String s = "Hello"; | |
String s1 = s.toString(); | s1 ="Hello" | ||
String toUpperCase() | String인스턴스에 저장되어 있는 모든 문자열을 대문자로 변환하여 반환한다. | String s ="Hello"; String s1 = s.toUpperCase(); | s1 = "HELLO" |
String trim() | 문자열의 왼쪽 끝과 오른쪽 끝에 있는 공백을 없앤 결과를 반환한다. 이 때 문자열 중간에 있는 공백은 제거되지 않는다. | String s = " Hello World "; String s1 = s.trim(); | s1="Hello World" |
static String valueOf(boolean b) static String valueOf(char c) static String valueOf(int i) static String valueOf(long i) static String valueOf(float f) static String valueOf(double d) static String valueOf(Object o) | 지정된 값을 문자열로 변환하여 반환한다. 참조변수의 경우 toString()을 호출한 결과를 반환한다. | String b = String.valueOf(true); String c = String.valueOf('a'); String i = String.valueOf(100); String l = String.valueOf(100L); String f = String.valueOf(10f); String d =String.valueOf(10.0); java.util.Date dd = new java.util.Data(); String date = String.valueOf(dd); | b="true", c="a", i="100", l="100", f = "10.0", d="10.0", date ="Wed Jan 27 21:26:29 KST 2016" |
✔ join()과 StringJoiner
join()
은 여러 문자열 사이에 구분자를 넣어서 결합한다.- 구분자로 문자열을 자르는
split()
과 반대의 작업을 한다고 생각하면 이해하기 쉽다.
✔ String.format()
format()
은 형식화된 문자열을 만들어내는 간단한 방법이다. printf()
하고 사용법이 완전히 똑같으므로 사용하는데 별 어려움은 없을 것이다.String str = String.format("%d더하기 %d는 %d입니다.", 3,5,3+5);
System.out.println(str); //3 더하기 5는 8입니다.
✔ 기본형 값을 String으로 변환
int i = 100;
String str1 =i +""; //100을 "100"으로 변환하는 방법1
String str2 = String.valueOf(i); //100을 "100"으로 변환하는 방법2
✔ String을 기본형 값으로 변환
int i = Integer.parseInt("100"); //"100"을 100으로 변환하는 방법1
int i2 = Integer.valueOf("100"); //"100"을 100으로 변환하는 방법2
기본형과 문자열간의 변환방법
기본형 ➡ 문자열 | 문자열 ➡ 기본형 |
---|---|
String String.valueOf(boolean b) String String.valueOf(char c) String String.valueOf(int i) String String.valueOf(long l) String String.valueOf(float f) String String.valueOf(double d) | boolean Boolean.parseBoolean(String s) byte Byte.parseByte(String s) short Short.parseShort(String s) long Long.parseLong(String s) float Float.parseFloat(String s) double Double.parseDouble(String s) |
trim()
: 문자열 양끝의 공백을 제거해주는 trim()
을 습관적으로 같이 사용하기도 한다.
int val = Integer.parseInt(" 123 ".trim()); // 문자열 양 끝의 공백을 제거 후 변환
substring(int start, int end)
를 사용할 때 주의해야 할 점은 매개변수로 사용되는 문자열에서 각 문자의 위치를 뜻하는 index
가 0부터 시작한다는 것과 start부터 end의 범위 중 end 위치에 있는 문자는 결과에 포함되지 않는 것이다.
ex)
String str = "Hello.java";
int index = str.indexOf('.'):
int index2 = str.lastIndexOf('.');
String a = str.substring(0,5);
String b =str.substring(6,10);
index = 5
index2 = 5
a = "Hello"
b ="java"
✔ StringBuffer의 생성자
StringBuffer
클래스의 인스턴스를 생성할 때, 적당한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편집하기 위한 공간(buffer
)로 사용된다.
public StringBuffer(int length){
value = new char[length];
shared =false;
}
public StringBuffer(){
this(16); //버퍼의 크기를 지정하지 않으면 버퍼의 크기는 16이된다.
}
public StringBuffer(String str){
this(str.length() + 16); //지정된 문자열의 길이보다 16이 더 크게 버퍼를 생성한다
append(str);
}
✔ StringBuffer의 변경
append()
는 반환타입이 StringBuffer
인데 자신의 주소를 반환한다. StringBuffer sb2 = sb.append("ZZ"): //sb의 내용뒤에 "zz"를 추가된다.
System.out.println(sb); //abc123zz
System.out.println(sb2); //abc123zz
내부적으로 sb, sb2는 모두 0x100주소를 가리키고 있다.
✔ StringBuffer의 비교
StringBuffer
클래스는 equals
메서드를 오버라이딩 하지 않아서 StringBuffer
클래스의 equals
메서드를 사용해도 등가비교연산자(==
)로 비교한 것과 같은 결과를 얻는다.
StringBuffer sb =new StringBuffer("abc");
StringBuffer sb2 =new StringBuffer("abc");
System.out.println(sb == sb2); //false
System.out.println(sb.equals(sb2)); //false
StringBuffer
인스턴스에 담긴 문자열을 비교하기 위해서는 StringBuffer
인스턴스에 toString()
을 호출해서 String인스턴스를 얻은 다음, 여기에 equals
메서드를 사용해서 비교해야 한다.
String s= sb.toString();
String s2 = sb2.toString();
System.out.println(s.equals(s2)); //true
public class StringBufferEx1 {
public static void main(String[] args) {
StringBuffer sb =new StringBuffer("abc");
StringBuffer sb2 =new StringBuffer("abc");
System.out.println("sb == sb2 ? "+(sb==sb2));
System.out.println("sb.equals(sb2) ? "+sb.equals(sb2));
//StringBuffer의 내용을 String으로 변환한다.
String s = sb.toString(); //String s = new String(sb)와 같다.
String s2 = sb2.toString();
System.out.println("s.equals(s2) ? "+s.equals(s2));
}
}
sb == sb2 ? false
sb.equals(sb2) ? false
s.equals(s2) ? true
✔ StringBuffer클래스의 생성자와 메서드
✔ StringBuilder란?
StringBuffer sb;
sb = new StringBuffer();
sb.append("abc");
➡
StringBulider sb;
sb = new StringBuilder();
sb.append("abc");
💡 참고
- 지금까지 작성해온 프로그램은 전부 싱글 쓰레드로 작성된 것이다.
✔ Math클래스의 메서드
primitive type
)변수도 어쩔 수 없이 객체로 다뤄야 하는 경우가 있다.
기본형 | 래퍼클래스 | 생성자 | 활용예 |
---|---|---|---|
boolean | Boolean | Boolean(boolean value) | |
Boolean(String s) | Boolean b= new Boolean(true); Boolean b2 = new Boolean("true"); | ||
char | Character | Character(char value) | Character c= new Character('a); |
byte | Byte | Byte(byte value) Byte(String s) | Byte b =new Byte(10); Byte b2 = new Byte("10"); |
short | Short | Short(short value), Short(String s) | Short s = new Short(10); Short s2 =new Short("10"); |
int | Integer | Integer(int value), Integer(String s) | Integer i = new Integer(100); Integer i2 = new Integer("100"); |
long | Long | Long(long value), Long(String s) | Long l =new Long(100); Long l2 = new Long("100"); |
float | Float | Float(double value), Float (float value) Float(String s) | Float f = new Float(1.0);Float f2 = new Float(1.0f); Float f3 = new Float("1.0f"); |
double | Double | Double(double value) Double(String s) | Double d = new Double(1.0); Double d2 =new Dobule("1.0") |
equals()
가 오버라이딩 되어 있어서 주소값이 아닌 객체가 가지고 있는 값을 비교한다.
✔ Number클래스
이 클래스는 추상클래스로 내부적으로 숫자를 멤버변수로 갖는 래퍼 클래스들의 조상이다.
✔ 문자열을 숫자로 변환하기
문자열 → 기본형 | 문자열 → 래퍼클래스 |
---|---|
byte b= Byte.parseByte("100"); | Byte b= Byte.valueOf("100") |
short a= = Short.parseShort("100"); | Short s= Short .valueOf("100") |
int i= Integer.parseInt("100"); | Integer i= Integer.valueOf("100") |
long l= Long.parseLong("100"); | Long l= Long.valueOf("100") |
float f= Float.parseFloat("3.14"); | Float f= Float.valueOf("3.14") |
double d= Double.parseDouble("3.14"); | Double d= Double .valueOf("3.14") |
✔ 오토박싱 & 언방식(autoboxing & unboxing)
기본형과 참조형 간의 덧셈이 가능하다.
컴파일 전의 코드
int i = 5;
Integer iObj = new Integer(7);
int sum = i + iObj;
컴파일 후의 코드
int i = 5;
Integer iObj = new Integer(7);
int sum = i + iObj.intValue();
Vector
클래스나 ArrayList
클래스에 기본형 값을 저쟁해야 할 때나 형변환이 필요할 때도 컴파일러가 자동적으로 코드를 추가해준다. autoboxing
)이라 하고, 반대로 변환하는 것은 언박싱(unboxing
)이라고 한다.
💡 참고
<Integer>
는 지네릭스(generics)
public class WrapperEx3 {
public static void main(String[] args) {
int i = 10;
//기본형을 참조형으로 형변환(형변환 생략 가능)
Integer intg = (Integer) i; //Integer intg =Integer.valueOf(i);
Object obj = (Object) i; //Object obj =(Object) Integer.valueOf(i);
Long lng = 100L; // Long Ing = new Long(100L);
int i2 = intg + 10; //참조형과 기본형간의 연산 가능
long l = intg + lng ; //참조형 간의 덧셈도 가능
Integer intg2 = new Integer(20);
int i3 = (int)intg2; //참조형을 기본형으로 형변환도 가능(형변환 생략가능)
Integer intg3 = intg2 * i3;
System.out.println("i ="+i);
System.out.println("intg ="+intg);
System.out.println("obj ="+obj);
System.out.println("lng ="+lng);
System.out.println("intg + 10= "+i2);
System.out.println("intg + lng ="+l);
System.out.println("intg2 ="+intg2);
System.out.println("i3 ="+i3);
System.out.println("intg2 + i3 ="+intg3);
}
}
i =10
intg =10
obj =10
lng =100
intg + 10 =20
intg + lng =110
intg2 =20
i3 =20
intg2 + i3 =40
autoboxing
)을 이용해서 기본형과 참조형간의 형변환과 연산을 수행하는 예를 보여준다.
컴파일 전의 코드
Integer intg = (Integer)i;
Object obj = (Object) i;
Long lng = 100L;
컴파일 후의 코드
Integer intg = Integer.valueOf(i);
Object obj = (Object) Integer.valueOf(i);
Long lng = new Long(100L);
✔️ requireNonNull()
requireNonNull()
은 해당 객체가 널이 아니어야 하는 경우에 사용한다. NullPointerException
을 발생시킨다. 두 번째 매개변수로 지정하는 문자열은 예외의 메시지가 된다.static <T> T requireNonNull(T, obj)
static <T> T requireNonNull(T, obj, String message)
static <T> T requireNonNull(T, obj, Supplier<String> messageSupplier)
✔ compate()
compare()
는 두 비교대상이 같으면 0, 크면 양수, 작으면 음수를 반환한다.static int compare(Object a, Object, b, Comparator c)
✔ equals(), deepEquals()
static boolean equals(Obejct a, Object b)
static boolean deepEquals(Object a, Object b)
equals()
의 내부에서 a와 b의 널 검사를 하기 때문에 따로 널 검사를 위한 조건식을 넣지 않아도 되는 것이다.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(Obejcts.deepEquals(str2D, str2D2)); //true
✔ toString()
static String toString(Object o)
static String toStirng(Object o, String nullDefault)
✔ hashCode()
static int hashCode(Object o)
static int hash(Object... values)
hashCode()
인데, 이것 역시 내부적으로 널 검사를 한 후에 Object클래스의 hashCode()
를 호출할 뿐이다. 단 널일 때는 0을 반환한다.
ab
, AB
를 비교한 결과가 0, 두 문자열이 같다는 결과가 나옴)
double ranNum = Math.random();
double ranNum = new Random().nextDouble(); //위의 문장과 동일
Math.random()
과 Random
의 가장 큰 차이점이라면, 종자값(seed)을 설정할 수 있다는 것이다.
✔ Random클래스의 생성자와 메서드
Random
은System.currentTimeMillis()
로 하기 때문에 실행할 때마다 얻는 난수가 달라진다.
System.currentTimeMillis()
는 현재 시간을 천분의 1초단위로 변환해서 반환한다.
Random
클래스에 nextBytes()
메서드가 있는데, 각 메서드가 반환하는 값의 범위와 nextBytes()
는 BigInteger(int signum, byte[] magnitude)
와 함께 사용하면 int
의 범위인 -2^31 ~ 2^31-1
보다 넓은 범위의 난수를 얻을 수 있다.메서드 | 설명 |
---|---|
Random() | System.현재시간을 종자값(seed)으로 이용하는 Random인스턴스를 생성한다. |
Random(long seed) | 매개변수 seed를 종자값으로 하는 Random인스턴스를 생성한다. |
boolean nextBoolean() | boolean타입의 난수를 반환한다. |
void nextBytes(byte[] bytes) | bytes배열에 byte타입의 난수를 채워서 반환한다. |
double nextDouble() | double 타입의 난수를 반환한다.(0.0≤x<1.0) |
float nextFloat()} float타입의 난수를 반환한다.(0.0≤x<1.0) | |
double nextGaussian() | 평균은 0.0이고 표준편차는 1.0인 가우시안(Gaussian)분포에 따른 double형의 난수를 반환한다. |
int nextInt() | int타입의 난수를 반환한다.(int의 범위) |
int nextInt(int n) | 0~n의 범위에 있는 int값을 반환한다.(n은 범위에 포함되지 않음) |
long nextLong() | long타입의 난수를 반환한다.(long의 범위) |
void setSeed(long seed) | 종자값을 주어진 값(seed)로 변경한다. |
Random
인스턴스는 시스템이나 실행시간 등에 관계없이 항상 같은 값을 같은 순서로 반환할 것을 보장한다.
✔ Math.random()을 이용해서 유용할만한 메서드들
public class RandomEx3 {
public static void main(String[] args) {
for(int i=0; i< 10; i++)
System.out.print(getRand(5,10)+",");
System.out.println();
int[] result = fillRand(new int[10], new int[]{2,3,7,5});
System.out.println(Arrays.toString(result));
}
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);
}
}
6,5,5,10,10,7,5,9,5,7,9
[3, 2, 2, 2, 5, 5, 5, 3, 3, 2]
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 모두 범위에 포함된다.
정규식이란 텍스트 데이터 중에서 원하는 조건(패턴, pattern)과 일치하는 문자열을 찾아내기 위해 사용하는것으로 미리 정의된 기호와 문자를 이용해서 작성한 문자열을 말한다.
- 자주 쓰이는 정규식의 작성 예를 보고 응용할 수 있을 정도까지만 학습하고 넘어가는 것이 좋다.
Pattern
은 정규식을 정의하는데 사용되고Matcher
은 정규식(패턴)을 데이터와 비교하는 역할을 한다.
Scanner
는 화면, 파일, 문자열과 같은 입력소스로부터 문자데이터를 읽어오는데 도움을 줄 목적으로 JDK1.5부터 추가되었다.Scanner
는 정규식 표현(Regular expression
)을 이용한 라인단위의 검색을 지원하며 구분자(delimiter
)에도 정규식 표현을 사용할 수 있어서 복잡한 형태의 구분자도 처리가 가능하다.
⚠️ 주의
- 실제 입력된 데이터의 형식에 맞는 메서드를 사용하지 않으면,
InputMismatchException
이 발생한다.
argArr = input.split(" +"); //입력받은 내용의 공백을 구분자로 자른다.
public class ScannerEx3 {
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(new File("data3.txt"));
int cnt = 0;
int totalSum = 0;
while(sc.hasNextInt()){
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
를 이용해서 각각의 데이터를 읽어야 한다.
StringTokenizer
는 긴 문자열을 지정된 구분자(delimiter
)를 기준으로 토큰(token
)이라는 여러 개의 문자열로 잘라내는데 사용된다.StringTokenizer
는 구분자로 단 하나의 문자밖에 사용하지 못하기 때문에 보다 복잡한 형태의 구분자로 문자열을 나누어야 할때는 어쩔수 없이 정규식을 사용하는 메서드를 사용해야 할 것이다.
✔ StringTokenizer의 생성자와 메서드
생성자/메서드 | 설명 |
---|---|
StringTokenizer(String str, String delim) | 문자열(str)을 지정된 구분자(delim)로 나누는 StringTokenizer를 생성한다. (구분자는 토큰으로 관리되지 않음) |
StringTokenizer(String str, String delim, boolean returnDelims) | 문자열(str)을 지정된 구분자(delim)로 나누는 StringTokenizer를 생성한다. returnDelims의 값을 true로 하면 구분자도 토큰으로 간주된다. |
int countTokens() | 전체 토큰의 수를 반환한다. |
boolean hasMoreToken() | 토큰이 남았는지 알려준다. |
String nextToken() | 다음 토큰을 반환한다. |
StringTokenizer
는 단 한 문자의 구분자만 사용할 수 있기 때문에, +-*/=()
전체가 하나의 구분자가 아니라 각각의 문자가 모두 구분자라는 것에 주의해야 한다.Scanner
나 String
클래스의 split
메서드를 사용해야 한다.
public class StringTokenizerEx3 {
public static void main(String[] args) {
String source = "1,김천재,100,100,100|2,박수재,95,80,90|3,이자바,80,90,90";
StringTokenizer st = new StringTokenizer(source, "|");
while(st.hasMoreTokens()){
String token = st.nextToken();
StringTokenizer st2 = new StringTokenizer(token, ",");
while(st2.hasMoreTokens())
System.out.println(st2.nextToken());
System.out.println("------");
}
}
}
|
를 사용하였고, 학생의 이름과 점수 등을 구분하기 위해 ,
를 사용하였다.StringTokenizer
은 빈 문자열을 토큰으로 인식하지 않는다.
BigInteger
는 내부적으로 int
배열을 사용해서 값을 다룬다. 그래서 long
타입보다 훨씬 큰 값을 다룰 수 있는 것이다. 대신 성능은 long
타입밖에 떨어질 수 밖에 없다.
final int signum; // 부호 1(양수), 0, -1(음수) 셋중 하나
final int[] mag; // 값(magnitude)
BigInteger
는 String
처럼 불변(immutable
)이다.signum
의 값이 -1, 즉 음수인 경우, 2의보수법에 맞게 mag
의 값을 변환해서 처리한다. 그래서 부호만 다른 두 값의 mag
는 같고 signum
은 다르다.BigInteger
이란 얘기는 새로운 인스턴스가 반환된다는 뜻이다. Java API를 보면, BigInteger
의 연산 메서드마다 연산기호가 적혀있기 때문에, 각 메서드가 어떤 연산자를 구현한 것인지 쉽게 알 수 있다.
BigInteger에 좀더 알고 싶을 때는 구글링 or Java의 정석 책보기