본문 바로가기

프로그래밍언어/JAVA

[JAVA] java.lang

반응형

java.lang 패키지는 자바 프로그래밍에서 가장 기본이 되는 클래스들을 포함하고 있다. Object String 과 같은 클래스들을 포함하고 있는데, 이 클래스들은 import 없이도 사용할 수 있다.

 

 

1. Object 클래스

 

Object 클래스는 자바의 모든 클래스의 최고 조상 클래스이다. 그렇기 때문에 Object 클래스의 멤버들은 모든 클래스에서 바로 사용이 가능하다.

 

메서드 설명
protected Object clone() 객체 자신의 복사본을 반환한다.

Object 클래스에 정의된 clone() 메서드는 단순히 인스턴스 변수의 값을 복사하기 때문에 참조 변수 타입의 인스턴스 변수가 정의되어 있는 클래스는 완전한 복사가 이루어지지 않는다. 그렇기 때문에 clone으로 복사한 객체의 수정이 원본 객체에 영향을 끼치게 된다.
이러한 상황을 해결하기 위해서는 새로운 객체를 생성하여 값을 복사하도록 오버라이딩 해야한다.
public boolean equals(Object obj) 객체 자신과 obj가 같은 객체인지 확인한다.

기본적으로 비교하는 참조변수들이 같은 객체(주소값 비교)를 참조하는지를 통해서 비교가 되는데, String과 같이 객체의 값을 비교해야하는 경우에는 오버라이딩을 통해서 구현하기도 한다.
protected void finalize() 객체가 소멸될 때 GC에 의해 호출된다.
만약 소멸 시에 수행해야 하는 작업이 있는 경우 오버라이딩해서 사용하기도 한다.
public Class getClass() 객체 자신의 클래스 정보를 담고있는 Class 인스턴스를 반환한다.

Class 클래스 타입의 객체를 반환한다. Class 객체는 클래스의 모든 정보를 가지고 있으며, 클래스당 단 1개만 존재한다. 그리고 클래스 파일이 ClassLoader 에 의해 메모리에 올라갈 때 자동적으로 생성된다.

클래스 로더는 실행 시에 필요한 클래스를 동적으로 메모리에 로드한다. 기존에 생성된 클래스 객체가 메모리에 있는지 확인하고 있으면 객체의 참조를, 없으면 클래스 패스에 있는 경로에서 클래스 파일을 찾아 해당 파일을 읽어서 Class 객체로 반환한다. 없는 경우 ClassNot FountException 이 발생된다.
public int hashCode() 객체 자신의 해시코드를 반환한다.

해시코드는 각 객체의 고유한 번호로 hashCode() 메서드는 객체의 주소값을 이용해서 해시코드를 만든다.

equals()를 통해 클래스의 인스턴스 변수 값으로 객체의 같고 다름을 판단할 때 hashCode() 도 적절히 오버라이딩 하여 같은 값에 대해 같은 해시코드를 반환하도록 해주어야 한다.
public String toString() 객체 자신의 정보를 문자열로 반환한다.

기본적으로 '클래스이름 + @ + 16진수 해시코드'의 형식으로 출력된다.

다른 형식으로 출력되기를 원한다면 오버라이딩해야한다.
public void notify() 객체 자신을 사용하려고 기다리는 스레드를 하나만 깨운다.
public void notifyAll() 객체 자신을 사용하려고 기다리는 스레드를 모두 깨운다.
public void wait() 다른 스레드가 notify()나 notifyAll()을 호출할 때까지 현재 스레드를 무한히 또는 지정된 시간동안 기다리게 한다.
public void wait(long timeout) public void wait() 메서드와 동일하다.
public void wait(long timeout, int nanos) public void wait() 메서드와 동일하다.

 

- 깊은 복사 (deep copy), 얕은 복사 (shallow copy)

 

clone() 메서드는 단순히 객체에 저장된 값을 복사할 뿐 객체가 참조하고 있는 객체까지 복제하지는 않는다. 그렇기 때문에 원본과 복제본이 같은 객체를 공유하게 되는 상황이 벌어진다. 이러한 복사를 얕은 복사 (shallow copy) 라고 한다. 얕은 복사에서는 원본과 복제본 간에 서로의 수정이 영향을 끼치게 된다.

 

반면에 원본이 참조하고 있는 객체까지 복제하여 새로운 객체를 생성하게 되는 것을 깊은 복사 (deep copy) 라고 한다. 깊은 복사는 원본과 복사본이 서로 다른 객체를 참조하기 때문에 서로간의 변경이 영향을 끼치지 않는다.

 

 

2. String 클래스


String 클래스는 자바에서 문자열을 저장하고 이를 다루는 클래스이다.

 

- immutable

 

String 클래스에는 문자열을 저장하기 위해서 문자형 배열 변수 char[] value를 인스턴스 변수로 정의해놓고 있다. 인스턴스 생성 시 생성자의 매개변수로 입력받는 문자열은 value 변수에 문자형 배열로 저장된다.

 

한번 생성된 String 인스턴스가 갖고 있는 문자열은 읽어 올 수만 있고, 변경할 수는 없다. 문자열의 결합과 같이 값이 수정되는 경우 기존의 value 가 수정되는 것이 아니라 매 연산 시 마다 새로운 문자열을 가진 String 인스턴스가 생성된다. 그렇기 때문에 문자열의 결합이나 추출 등 문자열을 다루는 작업이 많이 필요한 경우 String 클래스 대신 StringBuffer 클래스를 사용하는 것이 좋다.

 

- 문자열의 비교

 

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

 

String str1 = "abc";
String str2 = "abc";
String str3 = new String("abc");
String str4 = new String("abc");

 

아래의 3, 4번과 같이 생성자를 이용하는 경우는 new 연산자를 통해 메모리 할당이 이루어지기 때문에 항상 새로운 인스턴스가 생성된다. 그러나 문자열 리터럴은 이미 존재하는 것을 재사용하는 것이다.

 

equals() 를 사용했을 때는 두 문자열의 내용, 값을 비교하기 때문에 두 경우 모두 true로 결과를 얻는다. 하지만 각 String 인스턴스의 주소를 '==' 연산자로 비교했을 때는 결과가 다르다. 1, 2 번은 true를 결과로 출력하지만 3, 4 번은 false를 결과로 준다.

 

- 문자열 리터럴

 

자바 소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다. 이때 같은 내용의 문자열 리터럴은 한번만 저장된다. 문자열 리터럴도 String 인스턴스이고 한번 생성하면 내용을 변경할 수 없기 때문에 하나의 인스턴스를 공유하면 되기 때문이다.

 

class StringTest {
  public static void main(String args[]) {
    String s1 = "AAA";
    String s2 = "AAA";
    String s3 = "AAA";
  }
}

 

위의 예제에서 컴파일을 수행하면 StringTest.class 파일이 생성된다. 이 파일을 살펴보면 "AAA" 리터럴이 16진수 코드로 저장되어 있는 것을 발견할 수 있다. 위의 s1, s2, s3 변수 모두 이 리터럴을 참조하고 있다.

 

클래스 파일에는 소스파일에 포함된 모든 리터럴의 목록이 있다. 해당 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때, 이 리터럴의 목록에 있는 리터럴들이 JVM 내에 있는 상수 저장소 (constant pool)에 저장된다. 이때 이 곳에 "AAA" 와 같은 문자열 리터럴이 생성되어 저장된다.

 

- 빈 문자열 (empty string)

 

String 변수에 ""와 같이 빈 값을 저장하게 되는 경우 해당 변수의 내부에 new char[0] 과 같이 길이가 0인 char형 배열이 생성되어 저장되게 된다. 길이가 0이기 때문에 아무런 문자도 저장할 수 없는 배열이 된다.

 

- String 클래스의 생성자와 메서드

 

메서드 설명
String(String s) 주어진 문자열을 갖는 String 인스턴스를 생성한다.
String(char[] value) 주어진 문자열을 갖는 String 인스턴스를 생성한다.
String(StringBuffer buf) StringBuffer 인스턴스가 갖고 있는 문자열과 같은 내용의 String 인스턴스를 생성한다.
char charAt(int index) 지정된 위치(index)에 있는 문자를 알려준다. index는 0부터 시작된다.
int compareTo(String str) 문자열과 사전 순서로 비교한다. 같으면 0을 사전순으로 이전이면 1을, 이후면 -1을 반환한다.
String concat(String str) 문자열을 뒤에 덧붙인다.
boolean contains(CharSequence s) 지정된 문자열 s가 포함되었는지 검사한다.
boolean endsWith(String suffix) 지정된 문자열 suffix로 문자열이 끝나는지 검사한다.
boolean equals(Obejct obj) 매개변수로 받은 문자열 obj String 인스턴스의 문자열을 비교한다. obj String이 아니거나 문자열이 다르면 false를 반환한다.
boolean equalsIgnoreCase(String str) 문자열과 String 인스턴스의 문자열을 대소문자 구분없이 비교한다.
int indexOf(int ch) 주어진 문자 ch가 문자열에 존재하는지 확인하여 위치, index를 알려준다. 못찾으면 -1을 반환한다. index는 0부터 시작한다.
int indexOf(int ch, int pos) 주어진 문자 ch가 문자열에 존재하는지 지정된 위치, pos 부터 확인하여 위치를 알려준다. 못찾으면 -1을 반환한다. index는 0부터 시작한다.
int indexOf(String str) 주어진 문자열이 존재하는지 확인하여 그 위치, index를 반환한다. 없으면 -1을 반환한다. index는 0부터 시작한다.
String intern() 문자열을 상수풀, constant pool 에 등록한다. 이미 상수풀에 같은 내용의 문자열이 있을 경우 그 문자열의 주소값을 반환한다.
int lastIndexOf(int ch) 지정된 문자 또는 문자코드를 문자열의 오른쪽 끝에서부터 찾아서 위치, index를 찾아준다. 못찾으면 -1을 반환한다.
int lastIndexOf(String str) 지정된 문자열을 인스턴스의 문자열 끝에서부터 찾아서 위치, index를 알려준다. 못찾으면 -1을 반환한다.
int length() 문자열의 길이를 반환한다.
String replace(char old, char nw) 문자열의 문자 old를 새로운 문자 nw로 변환하여 반환한다.
String replace
(CharSequence old, CharSequence nw)
문자열 중의 문자열 old를 새로운 문자열 nw로 모두 바꾼 문자열을 반환한다.
String replaceAll
(String regex, String replacement)
문자열 중에서 저징된 문자열 regex와 일치하는 것을 모두 새로운 문자열 replacement로 변경한다.
String replaceFirst
(String regsx, String replacement)
문자열 중에서 지정된 문자열 regex와 일치하는 것 중 첫번째 것만 새로운 문자열 replacement 로 변경한다.
String[] split(String regex) 문자열을 지정된 분리자, regex로 나누어 문자열 배열에 담아 반환한다.
String split(String regex, int limit) 문자열을 지정된 분리자 regex로 나누어 문자열 배열에 담아 반환한다. 단, 문자열 전체를 지정된 수, limit으로 자른다.
boolean startsWith(String prefix) 주어진 문자열 prefix로 시작하는지 검사한다.
String substring(int begin),
String substring(int begin, int end)
begin부터 end 범위에 포함된 문자열을 얻는다. 이때 시작위치의 문자는 범위에 포함되지만 끝 위치의 문자는 포함되지 않는다. (begin <= x < end)
String toLowerCase() String 인스턴스에 저장되어있는 모든 문자열을 소문자로 변환하여 반환한다.
String toString() String 인스턴스에 저장된 문자열을 반환한다.
String toUpperCase() String 인스턴스에 저장되어있는 모든 문자열을 대문자로 변환하여 반환한다.
String trim() 문자열의 왼쪽 끝과 오른쪽 끝에 있는 공백을 없앤 결과를 반환한다. 이 때 문자열 중간에 있는 공백은 제거되지 않는다.
static String valueOf(...) 지정된 값을 문자열로 변환하여 반환한다. 참조변수의 경우, toString()을 호출한 결과를 반환한다. 매개변수로는 boolean, char, int, long, float, double, Object 타입의 변수가 들어갈 수 있다.

 

- join(), StringJoin

 

join()은 여러 문자열 사이에 구분자를 넣어서 결합한다. 또한 java.util.StringJoiner 클래스를 사용해서 문자열을 결합할 수도 있다.

 

String[] arr = {"aaa", "bbb", "ccc"};
String str = String.join("-", arr);
System.out.println(str); // aaa-bbb-ccc

StringJointer sj = new stringJoiner(",", "[", "]");
for(String s : arr) {
	sj.add(s.toUpperClass());
}

System.out.println(sj.toString()); // [AAA,BBB,CCC]

 

- 문자 인코딩 변환

 

getBytes(String charsetName)을 사용하면 문자열의 문자 인코딩을 다른 인코딩으로 변경할 수 있다. 자바가 UTF-16을 사용하지만, 문자열 리터럴에 포함되는 문자들은 OS의 인코딩을 사용한다. 한글 윈도우즈의 경우 문자 인코딩으로 CP949를 사용하며, UTF-8로 변경하려면 아래와 같이 한다.

 

byte[] utf8_str = "가".getBytes("UTF-8"); // 문자열을 UTF-8로 변환
String str = new String(utf8_str, "UTF-8"); // byte 배열을 문자열로 변환

 

서로 다른 문자 인코딩을 사용하는 컴퓨터 간에는 데이터를 주고받을 때 적절한 문자 인코딩이 필요하다. 이를 변환하지 않으면 문자가 깨져서 전달된다.

 

- String.format()

 

format()은 문자열의 형식을 만들어내는 간단한 방법이다.

 

String str = String.format("%d + %d = %d", 3, 5, 3 + 5);
System.out.println(str); // 3 + 5 = 8

 

 

3. StringBuffer, StringBuilder

 

String 클래스는 인스턴스를 생성할 때 지정된 문자열을 변경할 수 없는 immutable 클래스이지만 StringBuffer 클래스는 변경이 가능하다. 내부적으로 문자열 편집을 위한 버퍼를 가지고 있으며 StringBuffer 인스턴스를 생성할 때 그 크기를 지정할 수 있다. 이때 버퍼의 크기를 적절하게 잡아주는 것이 중요한데, 만약 작업 중에 버퍼를 넘어가는 경우 버퍼의 크기를 늘려주는 작업이 추가적으로 발생하기 때문이다.

 

StringBuffer 클래스는 String 클래스와 유사하게 char형 배열 value을 인스턴스 변수로 선언해 놓고 있다. StringBuffer 인스턴스가 생성될 때, char형 배열의 참조 변수를 인스턴스 변수로 선언해 놓고 이를 통해서 값을 저장한다.

 

- StringBuffer 의 생성자

 

StringBuffer 클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편집하기 위한 공간으로 사용한다. StringBuffer 생성자 StringBuffer(int length)를 사용해서 StringBuffer 인스턴스에 저장된 문자열의 길이를 설정하여 인스턴스를 생성할 수 있다.

 

class StringBufferExample {
  public static void main(String[] args) {
    StringBuffer sb = new StringBuffer("abc");
    System.out.println(sb.toString()); // abc
  }
}

 

- StringBuffer의 변경

 

이미 생성된 StringBuffer 인스턴스에 append() 메서드를 통해 값을 추가하면 인스턴스 변수인 value 배열에 해당 값이 추가가 된다. 그리고 append() 메서드는 StringBuffer 인스턴스의 주소를 반환한다. 그렇기 때문에 append() 메서드 뒤에 또 다시 append() 메서드를 붙여서 chaining 형식으로 연속적으로 메서드 호출이 가능하다.

 

StringBuffer sb = new StringBuffer("abc");
sb.append("bcbc").append("dede");
System.out.println(sb.toString()); // abcbcbcdede

 

- StringBuffer의 비교

 

String 클래스는 equals 메서드를 오버라이딩해서 문자열의 내용, 값을 비교하도록 구현되어 있지만 StringBuffer는 equals 메서드를 오버라이딩 하지 않아서 StringBuffer 클래스의 equals 메서드를 사용해도 등가비교연산자(==)로 비교한 것과 같은 결과를 얻는다.

 

StringBuffer sb1 = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
System.out.println(sb1.equals(sb2)); // false
System.out.println(sb1 == sb2); // false

 

- StringBuilder

 

StringBuffer는 멀티쓰레드에 안전하도록 동기화되어 있다. (thread-safe) 쓰레드 동기화를 지원하는 경우 성능이 떨어질 수 밖에 없다. 만약 멀티쓰레드로 작성된 프로그램이 아닌 경우 StringBuffer의 동기화가 불필요하게 성능만 떨어뜨리게 된다. 이때는 StringBuilder 클래스를 사용하면 된다.

 

StringBuilder 클래스는 StringBuffer 클래스와 똑같은 기능으로 작성되어 있는데, 소스코드에서 StringBuffer 대신 StringBuilder를 사용하도록 바꾸기만 하면 된다. 생성자를 제외한 나머지 메서드들은 동일하다.

 

 

4. Math 클래스

 

Math 클래스는 기본적인 수학계산에 유용한 메서드로 구성되어 있다. Math 클래스의 생성자는 접근 제어자가 private 이기 때문에 다른 클래스에서 Math 인스턴스를 생성할 수 없다. 이렇게 설계된 이유는 Math 클래스의 메서드는 모두 static 이기 때문에 인스턴스를 생성할 필요가 없기 때문이다. static 메서드 외에는 E, PI 두가지 상수를 가지고 있다.

 

- 올림, 버림, 반올림

 

소수점 n번째 자리에서 반올림한 값을 얻기 위해서는 round() 메서드를 사용해야 하는데, 이 메서드는 항상 소수점 첫째자리에서 반올림을 해서 정수값 (long) 을 결과로 돌려준다.

 

이러한 성질을 이용하여 원하는 자리 수에서 반올림한 값을 얻기 위해서는 원하는 자리수까지 10의 n제곱으로 곱한 후 round() 메서드로 반올림한 후 다시 10의 n제곱으로 나눠주면된다.

 

rint() 메서드도 round() 처럼 소수점 첫째자리에서 반올림하지만, 반환값이 double 이라는 차이가 있다. 또한 매개변수의 값이 음수일 때 결과도 차이가 난다. round() 는 소수점 첫째자리가 5일 때, 더 큰 값으로 반올림하기 때문에 -1이 되지만, rint() 는 첫째자리가 5를 초과하는 값일 때만 반올림한다. 그렇기 때문에 음수에서는 첫째자리가 5인 경우 버림이 발생한다.

 

System.out.println(round(-1.5)); // -1
System.out.println(rint(-1.5)); // -2.0
System.out.println(ceil(-1.5)); // -1.5
System.out.println(floor(-1.5)); // -2.0

 

- 예외를 발생시키는 메서드

 

메서드 이름에 'Exact'가 포함된 메서드들이 JDK1.8 부터 새로 추가되었다. 이들은 정수형 간의 연산에서 발생할 수 있는 오버플로우(overflow)를 감지하기 위한 것이다.

 

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 decrementExact(int a) // a--
int negateExact(int a) // -a
int toIntExact(long value) // (int)value

 

연산자는 단지 결과만을 반환할 뿐, 오버플로우가 발생했는지에 대해서는 알려주지 않는다. 그러나 위의 메서드들은 오버플로우 발생 시 ArithmeticException을 발생시킨다.

 

- 삼각함수와 지수, 로그

 

Math 클래스에는 이름에서 알 수 있듯이 수학 관련 메서드들이 많이 있다.

// sqrt(double a): 루트와 같이 제곱근을 계산해준다.
// pow(double a, int b): a의 b제곱을 계산해준다.
double c = sqrt(pow(x2-x1, 2) + pow(y2-y1, 2));

 

 

5. wrapper 클래스

 

객체지향 개념에서 모든 것은 객체로 다루어져야 한다. 하지만 자바에서는 8개의 기본형을 객체로 다루지 않는다. 그러나 이러한 기본형 변수들도 객체로 다뤄야 하는 경우가 있는데, 이때 사용되는 것이 래퍼 (wrapper) 클래스이다. 8개의 기본형을 대표하는 8개의 래퍼 클래스가 있는데, 이 클래스들을 이용하면 기본형 값을 객체로 다룰 수 있다.

 

기본형 wrapper 클래스 생성자
boolean Boolean Boolean(boolean value)
Boolean(String s)
char Character Character(char value)
byte Byte Byte(byte value)
Byte(String s)
short Short Short(short value)
Short(string s)
int Integer Integer(int value)
Integer(String s)
long Long Long(long value)
Long(String s)
float Float Float(double value)
Float(float value)

Float(String s)
double Double Double(double value)
Double(String s)

 

- Number 클래스

 

이 클래스는 추상클래스로 내부적으로 숫자를 멤버변수로 갖는 래퍼 클래스들의 조상이다. Byte, Short, Integer, Long, Float, Double, BigInteger, BigDecimal 등의 클래스를 자손 클래스로 가지고 있다.

 

- 문자열을 숫자로 변환하기

 

문자열을 숫자로 변환하는 방법으로는 다음의 방법들이 있다.

 

int num1 = new Integer("100").intValue(); // 생성자를 사용
int num2 = Integer.parseInt("100"); // String -> primitive type 변경
Integer num3 = Integer.valueOf("100"); // String -> wrapper class type 변경

 

JDK1.5 부터는 autoboxing 기능 때문에 반환값이 기본형일 때와 래퍼 클래스일 때의 차이가 없어졌다. 둘 간에 변환이 가능하다는 것이다. 성능만 조금 느릴 뿐 valueOf() 메서드를 사용해도 문제가 되진 않는다.

 

- autoboxing & unboxing

 

JDK1.5 이전에는 기본형과 참조형 간의 연산이 불가능했기 때문에, 래퍼 클래스로 기본형을 객체로 만들어서 연산해야 했다. 그러나 이제는 기본형과 참조형 간의 덧셈이 가능하다. 자바 컴파일러에서 자동으로 변환하는 코드를 넣어주기 때문이다.

 

컴파일 전 컴파일 후
int i = 5;
Integer obj = new Integer(7);



int sum = i + obj;
int i = 5;
Integer obj = new Integer(7);

int sum = i + obj.intValue();

 

Vector 클래스나 ArrayList 클래스에 기본형 값을 저장해야할 때나 형변환이 필요할 때도 컴파일러가 자동적으로 코드를 추가해 준다. 기본형 값을 래퍼 클래스의 객체로 자동 형변환 해주는 것을 오토박싱, autoboxing 이라고 하고, 반대로 변환하는 것을 언박싱, unboxing 이라고 한다.

반응형