java.lang패키지와 유용한 클래스

LeeKyoungChang·2022년 2월 26일
0
post-thumbnail

Java의 정석 의 책을 읽고 정리한 내용입니다.

 

📚 1. java.lang패키지

  • java.lang패키지의 클래스들은 import문 없이도 사용할 수 있다.

 

📖 A. Object클래스

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
  • String 클래스는 문자열의 내용이 같으면, 동일한 해시코드를 반환하도록 hashCode 메서드가 오버라이딩되어 있기 때문에, 문자열의 내용이 같은 str1과 str2에 대해 hashCode()를 호출하면 항상 동일한 해시코드 값을 얻는다.

 

✔ toString()

  • 이 메서드는 인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것이다. 인스턴스의 정보를 제공한다는 것은 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 뜻이다.
  • 클래스를 작성할 때 toString()을 오버라이딩하지 않는다면, toString()을 호출하면 클래스 이름에 16진수의 해시코드를 얻게 될 것이다.
  • String 클래스의 toString()String 인스턴스가 갖고 있는 문자열을 반환하도록 오버라이딩되어 있고, Date 클래스의 경우 Date 인스턴스가 갖고 있는 날짜와 시간을 문자열로 변환하여 반환하도록 오버라이딩되어 있다.

  • 이처럼 toString()은 일반적으로 인스턴스나 클래스에 대한 정보 또는 인스턴스 변수들의 값을 문자열로 변환하여 반환하도록 오버라이딩되는 것이 보통이다.

  • Object 클래스에 정의된 toString()의 접근제어자가 public이므로 Card클래스의 toString()의 접근제어자도 public으로 했다는 것을 눈여겨 보자.

 

✔ clone()

  • 자신을 복제하여 새로운 인스턴스를 생성한다.
  • Cloneable 인터페이스를 구현한 클래스에서만 clone()을 호출할 수 있다. 이 인터페이스를 구현하지 않고 clone()을 호출하면 예외가 발생한다.
  • clone()을 사용하려면, 먼저 복제할 클래스가 Cloneable인터페이스를 구현해야하고, clone()을 오버라이딩하면서 접근 제어자를 protected에서 public으로 변경한다. 그래야만 상속관계가 없는 다른 클래스에서 clone()을 호출할 수 있다.
  • Cloneable인터페이스가 구현되어 있다는 것은 클래스 작성자가 복제를 허용한다는 의미이다.

 

✔ 공변 반환타입

  • JDK 1.5부터 '공변 반환타입(covariant return type)'이라는 것이 추가되었는데, 이 기능은 오버라이딩할 떄 조상 메서드의 반환타입을 자손 클래스의 타입으로 변경을 허용하는 것이다.
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();  //얕은 복사
  • c1와 c2는 같은 Point인스턴스를 가리키게 되므로 완전한 복제라고 할 수 없다.

 

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]
  • 인스턴스 c1을 생성한 후에 얕은 복사로 c2를 생성하고, 깊은 복사로 c3를 생성하였다.
Circle c1 = new Circle(new Point(1,1), 2.0);
Circle c2 = c1.shallowCopy();
Circle c3 = c1.deepCopy();
  • c1,c2는 같은 Point인스턴스를 가리키고 있는 상황이고, c3은 새로운 Point인스턴스를 가리키는 상황이다.
  • 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객체를 이용해서 객체를 생성

 

📖 B. String클래스

String클래스는 문자열을 저장하고 이를 다루는데 필요한 메서드를 함께 제공한다.

 

✔ 변경 불가능한(immutable) 클래스

  • String클래스는 앞에 final이 붙어 있으므로 다른 클래스의 조상이 될 수 없다.
  • +연산자를 이용해서 문자열을 결합하는 경우 인스턴스내의 문자열이 바뀌는 것이 아니라 새로운 문자열("ab")이 담긴 String인스턴스가 생성되는 것이다.
String a = "a";
String b = "b"; 
String a = a + b; //새로운 문자열 참조 

 

  • 문자열간의 결합이나 추출 등 문자열을 다루는 작업이 많이 필요한 경우에는 String클래스 대신 StringBuffer클래스를 사용하는 것이 좋다. StringBuffer인스턴스에 저장된 문자열은 변경이 가능하므로 하나의 StringBuffer인스턴스만으로도 문자열을 다루는 것이 가능하다.

 

✔ 문자열의 비교
문자열 리터럴을 지정하는 방법, String클래스의 생성자를 사용해서 만드는 방법이 있다.

String str1 = "abc";  //문자열 리터럴 "abc"의 주소가 str1에 저장됨
String str2 = "abc";  //문자열 리터럴 "abc"의 주소가 str2에 저장됨
String str3 = new String("abc"); //새로운 String인스턴스를 생성
String str4 = new String("abc"); //새로운 String 인스턴스를 생성
  • String클래스의 생성자를 이용한 경우에는 new 연산자에 의해서 메모리할당이 이루어지기 때문에 항상 새로운 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"

 

📖 C. StringBuffer 클래스와 StringBuilder 클래스

✔ 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인데 자신의 주소를 반환한다.
  • 그래서 아래와 같은 문장이 수행되면, sb에 새로운 문자열이 추가되고 sb자신의 주소를 반환하여 sb2에는 sb의 주소인 0x100이 저장된다.
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클래스의 생성자와 메서드

  • 구글링 또는 Java의 정석 책보기

 

✔ StringBuilder란?

  • StingBuffer는 멀티쓰레드에 안전(thread safe)하도록 동기화 되어 있다.
  • 동기화가 StringBuffer의 성능을 떨어트린다.
  • StringBuffer에서 쓰레드의 동기화만 뺀 StringBuilder가 새로 추가되었다.
  • StringBuilder는 StringBuffer와 완전히 똑같은 기능으로 작성되어 있어서 StringBuffer대신 StringBuilder를 사용하도록 바꾸기만 하면된다.
  • 즉, StringBuffer 타입의 참조변수를 선언한 부분과 StringBuffer의 생성자만 바꾸면 된다는 말이다.
StringBuffer sb;
sb = new StringBuffer();
sb.append("abc");

StringBulider sb;
sb = new StringBuilder();
sb.append("abc");
  • StringBuffer도 충분히 성능이 좋기 때문에 성능향상이 반드시 필요한 경우를 제외하고는 기존에 작성한 코드에서 StringBuffer를 StringBuilder로 굳이 바꿀 필요는 없다.

 

💡 참고

  • 지금까지 작성해온 프로그램은 전부 싱글 쓰레드로 작성된 것이다.

 

✔ Math클래스의 메서드

  • 구글링 또는 Java의 정석 책보기

 

📖 D. 래퍼(wrapper) 클래스

  • 기본형(primitive type)변수도 어쩔 수 없이 객체로 다뤄야 하는 경우가 있다.
  • 래퍼(wrapper)클래스 : 클래스를 이용하면 기본형 값을 객체로 다룰 수 있다.
  • char형과 int형을 제외한 나머지는 자료형 이름의 첫 글자로 대문자로 한 것이 각 래퍼 클래스의 이름이다.

 

기본형래퍼클래스생성자활용예
booleanBooleanBoolean(boolean value)
Boolean(String s)Boolean b= new Boolean(true); Boolean b2 = new Boolean("true");
charCharacterCharacter(char value)Character c= new Character('a);
byteByteByte(byte value) Byte(String s)Byte b =new Byte(10); Byte b2 = new Byte("10");
shortShortShort(short value), Short(String s)Short s = new Short(10); Short s2 =new Short("10");
intIntegerInteger(int value), Integer(String s)Integer i = new Integer(100); Integer i2 = new Integer("100");
longLongLong(long value), Long(String s)Long l =new Long(100); Long l2 = new Long("100");
floatFloatFloat(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");
doubleDoubleDouble(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);

 

📚 2. 유용한 클래스

📖 A. java.util.Objects클래스

✔️ 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)
  • 이 메서드의 장점은 null검사를 하지 않아도 된다는 점에 있다.
  • 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)
  • 두번째 메서드는 o가 널일때, 대신 사용할 값을 지정할 수 있어서 유용하다.

 

✔ hashCode()

static int hashCode(Object o)
static int hash(Object... values)
  • hashCode()인데, 이것 역시 내부적으로 널 검사를 한 후에 Object클래스의 hashCode()를 호출할 뿐이다. 단 널일 때는 0을 반환한다.

 

  • Comparator는 문자열을 대소문자 구분하지 않고 비교할 때 사용한다. (ab, AB를 비교한 결과가 0, 두 문자열이 같다는 결과가 나옴)

 

📖 B. java.util.Random클래스

double ranNum = Math.random();
double ranNum = new Random().nextDouble();  //위의 문장과 동일
  • Math.random()Random의 가장 큰 차이점이라면, 종자값(seed)을 설정할 수 있다는 것이다.
  • 종자값이 같은 Random인스턴스들은 항상 같은 난수를 같은 순서대로 반환한다.
  • 같은 종자값을 넣으면 같은 난수를 얻게 된다.

 

✔ Random클래스의 생성자와 메서드

RandomSystem.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 모두 범위에 포함된다.

 

📖 C. 정규식(Regular Expression) -java.util.regex 패키지

정규식이란 텍스트 데이터 중에서 원하는 조건(패턴, pattern)과 일치하는 문자열을 찾아내기 위해 사용하는것으로 미리 정의된 기호와 문자를 이용해서 작성한 문자열을 말한다.

  • 자주 쓰이는 정규식의 작성 예를 보고 응용할 수 있을 정도까지만 학습하고 넘어가는 것이 좋다.
  • Pattern은 정규식을 정의하는데 사용되고 Matcher은 정규식(패턴)을 데이터와 비교하는 역할을 한다.

 

📖 D. java.util.Scanner 클래스

  • 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를 이용해서 각각의 데이터를 읽어야 한다.

 

📖 E. java.util.StringTokenizer 클래스

  • 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는 단 한 문자의 구분자만 사용할 수 있기 때문에, +-*/=() 전체가 하나의 구분자가 아니라 각각의 문자가 모두 구분자라는 것에 주의해야 한다.
  • 만일 구분자가 두 문자 이상이라면, ScannerString 클래스의 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은 빈 문자열을 토큰으로 인식하지 않는다.

 

📖 F. java.math.BigInteger클래스

BigInteger는 내부적으로 int 배열을 사용해서 값을 다룬다. 그래서 long 타입보다 훨씬 큰 값을 다룰 수 있는 것이다. 대신 성능은 long 타입밖에 떨어질 수 밖에 없다.

final int signum; // 부호 1(양수), 0, -1(음수) 셋중 하나
final int[] mag;  // 값(magnitude)
  • 위의 코드에서 알 수 있듯이, BigIntegerString처럼 불변(immutable)이다.
  • 위의 코드에서 알 수 있듯이 부호를 따로 저장하고 배열에는 값 자체만 저장한다.
  • 그래서 signum의 값이 -1, 즉 음수인 경우, 2의보수법에 맞게 mag의 값을 변환해서 처리한다. 그래서 부호만 다른 두 값의 mag는 같고 signum은 다르다.
  • 반환타입이 BigInteger이란 얘기는 새로운 인스턴스가 반환된다는 뜻이다. Java API를 보면, BigInteger의 연산 메서드마다 연산기호가 적혀있기 때문에, 각 메서드가 어떤 연산자를 구현한 것인지 쉽게 알 수 있다.

 

BigInteger에 좀더 알고 싶을 때는 구글링 or Java의 정석 책보기

profile
"야, (오류 만났어?) 너두 (해결) 할 수 있어"

0개의 댓글

관련 채용 정보