java.lang 패키지란?
자바 프로그래밍의 가장 기본이 되는 클래스들을 포함하는 패키지이다.
import 문 없이도 사용할 수 있게 되어 있다.
String 클래스나 System 클래스를 import 문 없이 사용할 수 있었던 이유이기도 하다.
자주 사용하는 클래스 몇 가지만을 골라서 학습하자.
Object 클래스
모든 클래스의 최고 조상 클래스이며,
Object 클래스의 멤버들은 모든 클래스에서 바로 사용 가능하다.
equals(Object obj)
public boolean equals(Object obj){
return (this == obj);
}
매개변수로 객체의 참조변수를 받아 비교하여 그 결과를 boolean 값으로 알려주는 역할을 한다.
두 객체의 같고 다름을 참조변수의 값으로 비교한다.
Object 클래스의 equals() 메서드는 결국 두 참조변수가 같은 객체를 참조하고 있는지,
즉 참조변수에 저장된 주소값이 같은지를 판단하는 메서드이다.
class MyClass{
}
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass();
System.out.println(myClass1.equals(myClass2)); //false!
따라서 서로 다른 두 클래스의 인스턴스를 equals로 비교하면 항상 false를 결과로 얻는다.
같은 클래스에서 두 개의 인스턴스를 생성해 비교해도 false를 결과로 얻는다.
그런데 만약 인스턴스가 가지는 인스턴스 변수 값으로 비교하고 싶다면 어떻게 해야할까?
class MyClass{
int value;
public MyClass(int value){
this.value = value;
}
public boolean equals(MyClass obj){
return (this.value == obj.value);
}
}
MyClass myClass1 = new MyClass(1);
MyClass myClass2 = new MyClass(1);
System.out.println(myClass1.equals(myClass2)); //true!!
그렇다 그냥 equals 메소드를 value 값을 비교하도록 만들어주면 된다.
//Object
public boolean equals(Object obj){
return (this == obj);
}
//MyClass
public boolean equals(MyClass obj){
return (this.value == obj.value);
}
그런데 위의 코드처럼 메서드를 새롭게 정의하면 이는 메소드 오버로딩일까 오버라이딩일까?
MyClass myClass1 = new MyClass(1);
MyClass myClass2 = new MyClass(1);
System.out.println(myClass1.equals(myClass2)); //true
Object myObj1 = myClass1;
Object myObj2 = myClass2;
System.out.println(myObj1.equals(myObj2)); //false
정답은 오버로딩이다.
메서드의 매개변수가 Object에서 MyClass로 달라졌기 때문에 새로운 메서드가 하나 생긴것이다.
오버라이딩이었을 경우 Object 클래스로 형변환한후 호출해도 같은 메서드가 나왔을 것이다.
그럼 equals 메서드를 오버라이딩해 완전히 대체하고 싶다면 어떻게 해야할까?
public boolean equals(Object obj){
if (obj instanceof MyClass)
return (this.value == (MyClass)obj.value);
else
return false;
}
다음과 같이 Object 매개변수를 받도록 한 후 메서드 내에서 형변환을 하면 된다.
String 클래스는 Object 클래스의 equals 메서드를 사용하는 것이 아니라
이 같은 오버라이딩을 통해 String 인스턴스가 가지는 문자열 값을 직접 비교하도록 되어 있다.
그래서 동일한 내용의 문자열을 같은 두 String 인스턴스는 equals에서 항상 true가 나온다.
hashCode()
해싱 기법에 사용되는 ‘해시함수’를 구현한 메서드이다.
Object 클래스에 정의된 hashCode 메서드는 객체의 주소값으로 해시코드를 만들어 반환한다.
그런데 앞서 살펴본것처럼 클래스의 멤버변수 값으로 객체의 같고 다름을 판단해야 하는 상황이라면?
equals 메서드 뿐 아니라 hashCode 메서드도 적절히 오버라이딩해야한다.
같은 객체라면 같은 해시코드 값을 반환해야하기 때문이다.
특히 HashMap, HashSet과 같은 클래스에 저장할 객체라면 반드시 오버라이딩해야 하는 메서드이다.
String 클래스는 hashCode 메서드 역시 오버라이딩 되어
문자열 내용이 같은 String 인스턴스는 같은 해시코드 값이 나오도록 되어있다.
toString()
인스턴스에 대한 정보를 문자열로 제공할 목적으로 정의된 메서드이다.
기본 구현은 지난 시간에 살짝 확인했지만 다음과 같다.
public String toString(){
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
오버라이딩하지 않는다면 “클래스이름@16진수해시코드” 가 반환된다.
clone()
이 메서드는 자신을 복제하여 새로운 인스턴스를 생성하는 일을 한다.
Object 클래스에 정의된 clone()은 단순히 인스턴스 변수의 값만 복사한다.
따라서 참조타입의 인스턴스변수가 있다면 완전한 복사가 이루어지지 않는다.
만약 참조타입의 인스턴스 변수까지 완벽하게 복사하는 clone() 메서드를 만들고 싶다면 오버라이딩해서 사용하면 된다.
배열, java.tuil 패키지의 Vector, ArrayList, LinkedList, HashMap 등등의 많은 클래스들이 clone() 메서드를 오버라이딩해 깊은 복사를 제공한다.
얕은 복사와 깊은 복사에 관한 내용은 다 알거라고 생각해서 여기서 다루지 않겠다.
getClass()
이 메서드는 자신이 속한 클래스의 Class객체를 반환한다.
Class 객체는 이름이 ‘Class’인 클래스의 객체이다.
Class 객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재한다.
클래스 파일이 ‘클래스 로더’에 의해 메모리로 올라갈때, 자동으로 생성된다.
클래스의 정보가 필요할때 Class 객체에 대한 참조를 얻어와서 알 수 있는데,
- Class cObj = new MyClass().getClass()
- Class cObj = MyClass.class
- Class cObj = Class.forName(”MyClass”)
다음과 같은 방법이 있다.
이걸 어디에 사용하나 싶지만 클래스 객체는 클래스에 대한 모든 정보를 가지고 있기 때문에
동적인 객체 생성과 활용에 사용되며 Reflection API에서도 사용된다.
Refelction API란?
구체적인 클래스 타입을 알지 못해도 그 클래스의 정보에 대해 접근할 수 있게 해주는 API이다.
스프링의 빈과 Hibernate에서도 Refelction을 사용하고 있다.
스프링 Data JPA에서 Entity에 기본 생성자가 필요한 이유도 Refelction을 이용해 동적으로 객체를 생성해줘야 하는데
생성자 정보는 알 수 없기 때문이다.
이 부분은 나중에 따로 게시글을 하나 만들어 정리해보면 좋을 듯 하다.
https://tecoble.techcourse.co.kr/post/2020-07-16-reflection-api/
Reflection API 간단히 알아보자.
Spring Framework를 학습하다 보면 Java Reflection API를 자주 접하게 된다. 하지만 Reflection API…
tecoble.techcourse.co.kr
String 클래스
기존의 다른 언어에서는 문자열을 char 형의 배열로 다루나, 자바는 문자열을 위한 클래스를 제공한다.
String 클래스는 문자열을 저장하고, 이를 다루는데 필요한 메서드를 함께 제공한다.
String 클래스의 특징 - 변경 불가능한(immutable) 클래스
public final class String implements ...{
private char[] value;
...
}
String 클래스에는 문자열을 저장하기위해
문자형 배열 참조변수 char[] value를 인스턴스 변수로 정의해놓고 있다.
인스턴스 생성 시 생성자의 매개변수로 입력받는 문자열은 value에 문자형 배열로 저장된다.
한 번 생성된 String 인스턴스가 가지는 문자열은 읽어올 수만 있고, 변경할 수는 없다.
String a = "a";
String b = "b";
a = a + b; //"ab"
다음과 같이 문자열 연산 등을 통해 문자열을 바꾸는 것처럼 보여도
사실은 “ab”라는 새로운 String 인스턴스가 생성되어 대체되는 것이다.
이러한 내용은 #6 클래스 에서 String constant pool을 설명할때에도 잠깐 나왔었다.
책에서는 이와 같은 덧셈연산자를 사용해 문자열을 결합하는 것은
매 연산마다 새로운 문자열을 가진 String 인스턴스를 생성하므로,
가능한 결합 횟수를 줄이는 것이 좋다고 이야기한다.
문자열간의 결함, 추출등 문자열을 다루는 작업이 많이 필요한 경우는 StringBuffer 클래스를 사용하는 것이 좋다고도 이야기한다.
문자열의 비교
앞서 설명했듯 equals() 메서드를 이용하면 두 문자열의 내용을 비교한다고 이야기했다.
문자열의 비교에서 '==' 메서드를 이용한다면 어떻게 될까?
String str1 = "hi";
String str2 = "hi";
System.out.println(str1 == str2); //true
String str3 = new String("hi");
String str4 = new String("hi");
System.out.println(str3 == str4); //false
이 경우는 문자열 리터럴로 문자열을 생성했을때와 new 메서드로 문자열을 생성했을때 결과가 다르게 나온다.
이는 전에 6장에서 설명했듯이 문자열 리터럴의 저장방식 때문이다.
빈 문자열
길이가 0인 배열이 존재할 수 있을까? 답은 '그렇다' 이다.
char형 배열도 길이가 0인 배열을 생성할 수 있고, 이 배열을 내부적으로 가지고 있는 문자열이 빈 문자열이다.
String s = “” 와 같은 문장이 있을 때 만들어진다.
String.format()
String str1 = String.format("%d + %d = %d 입니다", 1,2,3);
System.out.println(str1); //1 + 2 = 3 입니다
format()은 형식화된 문자열을 만드는 간단한 방법이다.
C언어의 printf()와 사용법이 완전히 같다.
기본형 값을 String으로 변환
숫자로 이루어진 문자열을 숫자로, 또는 그 반대로 변환하는 경우가 자주 있다.
기본형을 문자열로 변경하는 방법은 숫자에 빈 문자열 “”를 더해주기만 하면 된다.
이외에도 valueOf()를 사용하는 방법도 있다.
String str1 = 123 + "";
String str2 = String.valueOf(123);
String을 기본형 값으로 변환
반대로 String을 기본형으로 변환하는 방법도 간단하다.
valueOf()를 쓰거나, 래퍼 클래스의 parseInt() 등의 메서드를 사용하면 된다.
int i = Integer.valueOf("123");
int j = Integer.parseInt("123");
래퍼 클래스에 대해선 후에 더 자세히 설명할 예정이다.
substring()
String str = "Hello world";
System.out.println(str.substring(0,5)); //Hello
한 문자열에서 내용의 일부를 추출하는 메서드이다.
sutstring(int start, int end) 으로 사용하며 문자의 위치인 index는 0부터 시작한다.
end위치의 문자는 포함되지 않는다.
래퍼 클래스
객체지향 개념에서 모든 것은 객체로 다뤄져야한다.
그러나 자바에서는 보다 높은 성능을 위해 8개의 기본형을 객체로 다루지 않는다.
하지만 때로는 기본형 변수도 어쩔 수 없이 객체로 다뤄야 하는 경우가 있다.
(매개변수로 객체를 요구할 때, 기본형 값이 아닌 객체로 저장해야할때, 객체간의 비교가 필요할 때 등등..)
이 경우에는 객체로 값을 전환하여 작업을 수행해야 하는데 이때 사용되는 것이 래퍼 클래스이다.
public final class Integer extends ...{
private int value;
...
}
래퍼 클래스는 대부분 자료형 이름의 첫 글자를 대문자로 해 이름지으며
객체생성시에 인자로 주어진 각 자료형에 알맞은 값을 내부적으로 저장해두고 있다.
래퍼클래스들은 모두 equals()가 오버라이딩 되어 주소값이 아닌 객체의 value 값을 비교한다.
또한 toString()도 구현되어있어, 문자열로 쉽게 변환이 가능하다.
문자열을 숫자로 변환하기.
String numberString = "100";
int i = new Integer(numberString).intValue();
int i = Integer.parseInt(numberString);
Integer i = Integer.valueOf(numberString);
다음과 같은 방법을 사용할 수 있는데 valueOf 메서드는 반환값이 기본형이 아닌 래퍼 클래스이다.
그러나 JDK 1,5 부터 오토박싱 기능이 도입되어,
반환값이 기본형일때와 래퍼 클래스일 때 차이가 없어졌다.
오토박싱&언박싱이란?
JDK 1.5에 추가된 기능으로 기본형과 참조형 간의 연산 등을 위해 형변환을 해야할 경우
컴파일러가 자동으로 코드를 추가해줘 형변환을 해준다.
기본형 값을 래퍼 클래스로 변환해주는 것을 오토박싱, 반대를 언박싱이라고 한다.
즉 int i = Integer.valueOf(numberString); 으로 사용해도 문제가 없다.
유용한 클래스
java.util.Objects 클래스
Object 클래스의 보조 클래스로 Math클래스처럼 모든 메서드가 static이다.
객체의 비교나 null 체크에 유용하다.
isNull()은 해당 객체가 널인지 확인해서 null이면 true를 반환하고 아니면 false를 반환한다.
nonNull()은 isNull()과 반대의 로직을 수행한다.
void setName(String name){
this.name = Object.requireNonNull(name, "name must not be null.");
}
requireNonNull()은 해당 객체가 널이 아니어야 하는 경우에 사용한다.
만일 객체가 null이면 NullPointerException을 발생시킨다.
if문을 이용한 null 체크를 하지 않도록 해준다.
Object 클래스에는 두 객체가 동일한지 비교를하기 위한 equals()만 존재한다.
두 객체의 대소비교를 위한 compare() 메서드를 Objects 클래스에서 제공한다.
static int compare(Object a, Object b, Comparator c)
static boolean equals(Object a, Object b)
static boolean deepEquals(Object a, Object b)
compare()는 두 비교대상이 같으면 0, 첫번째 오브젝트가 크면 양수, 작으면 음수를 반환한다.
또한 null 검사를 생략하고 사용할 수 있는 equals() 메서드와
재귀적으로 다차원 배열까지 비교하는 deepEquals() 메서드 또한 제공한다.
java.util.Random클래스
난수를 얻는 방법은 크게 두 가지가 있다.
- Math.random()
- new Random().nextDouble(), nextInt()
두 방식 모두 동일하게 동작한다.
Random()은 종자값을 받아 인스턴스를 생성할 수 있는데,
종자값이 같으면 항상 같은 난수를 순서대로 반환한다.
종자값을 넣지 않고 생성한 기본 생성자는 현재시간을 종자값으로 넣으므로
실행할때마다 얻는 난수가 달라진다.
nextInt(int n)은 0부터 n-1까지의 정수중 하나를 반환한다.
사실 이외에도 정규식을 다르는 java.util.regex 패키지
Scanner 클래스
Math 클래스
SpringTokenizer 클래스 등등이 더 자세하게 나와있지만
이걸 모두 암기할 필요까진 없을 것 같고,
이런 클래스들이 존재하며, 상황에 맞춰서 검색해보고 사용할 수 있으면 될 것 같아서
유용한 클래스 들에 대한 정리는 여기서 짧게 마치려고 한다.
'JAVA' 카테고리의 다른 글
JAVA의 정석 정독하기 #11 - Collection Framework (0) | 2023.11.17 |
---|---|
JAVA의 정석 정독하기 #10 - 날짜와 시간 (0) | 2023.11.15 |
JAVA의 정석 정독하기 #8 - 예외처리 (1) | 2023.11.14 |
JAVA의 정석 정독하기 #7 - 상속과 다형성 (1) | 2023.11.12 |
JAVA의 정석 정독하기 #6 - Class (1) | 2023.11.11 |