Java.lang패키지는 자바프로그래밍에 가장 기본이 되는 클래스들을 포함하고 있다. 그렇기 때문에 import문 없이 사용할 수 있게 되어있다.
-> finalize()는 메모리가 부족해서 가비지 컬렉터가 일을 해야 하는데 finalize() 메서드가 실행되면서 또 메모리를 먹고 있기 때문에 사용을 지양하자.
<예제 9-1 >
✍️ 입력
class Ex9_1 {
public static void main(String[] args) {
Value v1 = new Value(10);
Value v2 = new Value(10);
if (v1.equals(v2))
System.out.println("v1과 v2는 같습니다.");
else
System.out.println("v1과 v2는 다릅니다.");
} // main
}
class Value {
int value;
Value(int value) {
this.value = value;
}
}
💻 출력
v1과 v2는 다릅니다.
-> 두 객체의 참조변수(주소값)을 비교하기 때문에 다를 수 밖에 없다.
객체의 멤버변수를 비교하려면 오버라이딩 해야한다.
<예제 9-2 >
✍️ 입력
class Person {
long id;
public boolean equals(Object obj) {
if(!(obj instanceof Person)) return false;
return id ==((Person)obj).id;
}
Person(long id) {
this.id = id;
}
}
class Ex9_2 {
public static void main(String[] args) {
Person p1 = new Person(8011081111222L);
Person p2 = new Person(8011081111222L);
if(p1.equals(p2))
System.out.println("p1과 p2는 같은 사람입니다.");
else
System.out.println("p1과 p2는 다른 사람입니다.");
}
}
💻 출력
p1과 p2는 같은 사람입니다.
-> 해시코드가 같은 두 객체가 존재 할 수 있다.
-> 인스턴스변수 값으로 객체가 같고 다름을 판단해야 할 경우라면 equals메서드 뿐 만아니라 hashCode메서드도 오버라이딩 해야한다.
<예제 9-3 >
✍️ 입력
class Ex9_3 {
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
705927765
366712642
인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것이다. 인스턴스의 정보를 제공한다는 것은 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 뜻이다.
toString()을 오버라이딩하지 않는다면, 클래스이름과 16진수의 해시코드를 얻게 될 것이다.
<예제 9-4 >
✍️ 입력
class Card {
String kind;
int number;
Card() {
this("SPADE", 1);
}
Card(String kind, int number) {
this.kind = kind;
this.number = number;
}
}
class Ex9_4 {
public static void main(String[] args) {
Card c1 = new Card();
Card c2 = new Card();
System.out.println(c1.toString());
System.out.println(c2.toString());
}
}
💻 출력
Card@2a139a55
Card@15db9742
-> toString()을 오버라이딩 하지 않았기 때문에 Object클래스의 toString() 반환값에 따라 다음과 같이 다른 객체의 정보값이 나온다.
<예제 9-5 >
✍️ 입력
class Card2 {
String kind;
int number;
Card2() {
this("SPADE", 1); // Card(String kind, int number)를 호출
}
Card2(String kind, int number) {
this.kind = kind;
this.number = number;
}
public String toString() {
return "kind : " + kind + ", number : " + number;
}
}
class Ex9_5 {
public static void main(String[] args) {
Card2 c1 = new Card2();
Card2 c2 = new Card2("HEART", 10);
System.out.println(c1.toString());
System.out.println(c2.toString());
}
}
💻 출력
kind : SPADE, number : 1
kind : HEART, number : 10
-> toString()을 오버라이딩하여 보다 쓸모 있는 정보를 제공한다.
-> '+'를 사용해서 문자열을 결합하는 것은 맨 연산마다 String인스턴스가 생성되어 메모리공간을 차지하게 되므로 가능한 결합횟수를 줄이는 것이 좋다.
-> 문자열 작업이 많이 필요한 경우 StringBuffer클래스를 사용하는 것이 좋다.
why? StringBuffer인스턴스에 저장된 문자열은 변경이 가능하기 때문이다.
<예제 9-6 >
✍️ 입력
class Ex9_6 {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
System.out.println("String str1 = \"abc\";");
System.out.println("String str2 = \"abc\";");
System.out.println("str1 == str2 ? " + (str1 == str2));
System.out.println("str1.equals(str2) ? " + str1.equals(str2));
System.out.println();
String str3 = new String("abc");
String str4 = new String("abc");
System.out.println("String str3 = new String(\"abc\");");
System.out.println("String str4 = new String(\"abc\");");
System.out.println("str3 == str4 ? " + (str3 == str4));
System.out.println("str3.equals(str4) ? " + str3.equals(str4));
}
}
💻 출력
String str1 = "abc";
String str2 = "abc";
str1 == str2 ? true
str1.equals(str2) ? true
String str3 = new String("abc");
String str4 = new String("abc");
str3 == str4 ? false
str3.equals(str4) ? true
-> '=='는 주소값 비교, 'equals()'는 내용을 비교
문자열 리터럴은 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때, 클래스 파일의 리터럴들이 JVM내에 있는 '상수 저장소(constant pool)'에 저장된다. 이 때, "AAA"와 같은 문자열 리터럴은 자동적으로 생성되어 저장되는 것이다.
<예제 9-7 >
✍️ 입력
class Ex9_7 {
public static void main(String args[]) {
String s1 = "AAA";
String s2 = "AAA";
String s3 = "AAA";
String s4 = "BBB";
}
}
💻 출력
없음.
<예제 9-8 >
✍️ 입력
class Ex9_8 {
public static void main(String[] args) {
// 길이가 0인 char배열을 생성한다.
char[] cArr = new char[0]; // char[] cArr = {};와 같다.
String s = new String(cArr); // String s = new String("");와 같다.
System.out.println("cArr.length="+cArr.length);
System.out.println("@@@"+s+"@@@");
}
}
💻 출력
cArr.length=0
@@@@@@
-> 빈 문자열이 가능하며 문자열을 초기화 할 때 ""(문자의 개수가 0인 문자배열)을 쓰고, 문자를 초기화 할 땐 ' '(공백)을 사용하자.(문자는 빈 문자가 안 됨!)
<예제 9-9 >
✍️ 입력
import java.util.StringJoiner;
class Ex9_9 {
public static void main(String[] args) {
String animals = "dog,cat,bear";
String[] arr = animals.split(",");
System.out.println(String.join("-", arr));
StringJoiner sj = new StringJoiner("/","[","]");
for(String s : arr)
sj.add(s);
System.out.println(sj.toString());
}
}
💻 출력
dog-cat-bear
[dog/cat/bear]
-> for(String s : arr) : arr배열에서 하나씩 꺼내서 s에 넣는다.
방법 1은 가독성이 좋으니 극한으로 성능을 올리는게 아니라면 방법 1을 사용하자.
valueof()는 참조변수의 값을 반환한다. 그러나 오토박싱에 의해서 기본 자료형으로 받을 수 있다.
<예제 9-10 >
✍️ 입력
class Ex9_10 {
public static void main(String[] args) {
int iVal = 100;
String strVal = String.valueOf(iVal); // int를 String으로 변환한다.
double dVal = 200.0;
String strVal2 = dVal + ""; // int를 String으로 변환하는 또 다른 방법
double sum = Integer.parseInt("+"+strVal)
+ Double.parseDouble(strVal2);
double sum2 = Integer.valueOf(strVal) + Double.valueOf(strVal2);
System.out.println(String.join("",strVal,"+",strVal2,"=")+sum);
System.out.println(strVal+"+"+strVal2+"="+sum2);
}
}
💻 출력
100+200.0=300.0
100+200.0=300.0
-> + "" 을 해주는 경우는 String 객체를 계속 생성해서 더해주는 거라서 효율적이지 않다.
-> join()을 사용해서 메모리를 효율적으로 관리하자.
<예제 9-11 >
✍️ 입력
class Ex9_11 {
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는 equals()를 오버라이딩 하고 있지 않기 때문에 주소값을 비교한다.
-> 같은 내용으로 비교하고 싶다면 toString()을 통해 문자열로 바꾼 후 equals()를 사용하자.
<예제 9-12 >
✍️ 입력
class Ex9_12 {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("01");
StringBuffer sb2 = sb.append(23);
sb.append('4').append(56);
StringBuffer sb3 = sb.append(78);
sb3.append(9.0);
System.out.println("sb ="+sb);
System.out.println("sb2="+sb2);
System.out.println("sb3="+sb3);
System.out.println("sb ="+sb.deleteCharAt(10));
System.out.println("sb ="+sb.delete(3,6));
System.out.println("sb ="+sb.insert(3,"abc"));
System.out.println("sb ="+sb.replace(6, sb.length(), "END"));
System.out.println("capacity="+sb.capacity());
System.out.println("length="+sb.length());
}
}
💻 출력
sb =0123456789.0
sb2=0123456789.0
sb3=0123456789.0
sb =01234567890
sb =01267890
sb =012abc67890
sb =012abcEND
capacity=18
length=9
<예제 9-13 >
✍️ 입력
import static java.lang.Math.*;
import static java.lang.System.*;
class Ex9_13 {
public static void main(String args[]) {
double val = 90.7552;
out.println("round("+ val +")="+round(val));// 반올림
val *= 100;
out.println("round("+ val +")="+round(val));// 반올림
out.println("round("+ val +")/100 =" + round(val)/100); // 반올림
out.println("round("+ val +")/100.0=" + round(val)/100.0); // 반올림
out.println();
out.printf("ceil(%3.1f)=%3.1f%n", 1.1, ceil(1.1)); // 올림
out.printf("floor(%3.1f)=%3.1f%n", 1.5, floor(1.5)); // 버림
out.printf("round(%3.1f)=%d%n", 1.1, round(1.1)); // 반올림
out.printf("round(%3.1f)=%d%n", 1.5, round(1.5)); // 반올림
out.printf("rint(%3.1f)=%f%n", 1.5, rint(1.5)); // 반올림
out.printf("round(%3.1f)=%d%n", -1.5, round(-1.5)); // 반올림
out.printf("rint(%3.1f)=%f%n", -1.5, rint(-1.5)); // 반올림
out.printf("ceil(%3.1f)=%f%n", -1.5, ceil(-1.5)); // 올림
out.printf("floor(%3.1f)=%f%n", -1.5, floor(-1.5)); // 버림
}
}
💻 출력
round(90.7552)=91
round(9075.52)=9076
round(9075.52)/100 =90
round(9075.52)/100.0=90.76
ceil(1.1)=2.0
floor(1.5)=1.0
round(1.1)=1
round(1.5)=2
rint(1.5)=2.000000
round(-1.5)=-1
rint(-1.5)=-2.000000
ceil(-1.5)=-1.000000
floor(-1.5)=-2.000000
객체지향 개념에서 모든 것은 객체로 다루어져야 한다. 그러나 자바에서는 8개의 기본형을객체로 다루지 않는다. 대신 높은 성능을 얻을 수 있다.
때로는 기본형 변수도 어쩔 수 없이 객체로 다뤄야 할 경우가 있는데 이때 사용되는 것이 래퍼(wrapper)클래스이다.
<예제 9-14 >
✍️ 입력
class Ex9_14 {
public static void main(String[] args) {
Integer i = new Integer(100);
Integer i2 = new Integer(100);
System.out.println("i==i2 ? "+(i==i2));
System.out.println("i.equals(i2) ? "+i.equals(i2));
System.out.println("i.compareTo(i2)="+i.compareTo(i2));
System.out.println("i.toString()="+i.toString());
System.out.println("MAX_VALUE="+Integer.MAX_VALUE);
System.out.println("MIN_VALUE="+Integer.MIN_VALUE);
System.out.println("SIZE="+Integer.SIZE+" bits");
System.out.println("BYTES="+Integer.BYTES+" bytes");
System.out.println("TYPE="+Integer.TYPE);
}
}
💻 출력
i==i2 ? false
i.equals(i2) ? true
i.compareTo(i2)=0
i.toString()=100
MAX_VALUE=2147483647
MIN_VALUE=-2147483648
SIZE=32 bits
BYTES=4 bytes
TYPE=int
문자열 -> 기본형
ex) int i = Integer.ParseInt("100");
문자열 -> 래퍼 클래스
ex) Integer i = Integer.valueOf("100");
<예제 9-15 >
✍️ 입력
class Ex9_15 {
public static void main(String[] args) {
int i = new Integer("100").intValue();
int i2 = Integer.parseInt("100");
Integer i3 = Integer.valueOf("100");
int i4 = Integer.parseInt("100",2);
int i5 = Integer.parseInt("100",8);
int i6 = Integer.parseInt("100",16);
int i7 = Integer.parseInt("FF", 16);
// int i8 = Integer.parseInt("FF"); // NumberFormatException발생
Integer i9 = Integer.valueOf("100",2);
Integer i10 = Integer.valueOf("100",8);
Integer i11 = Integer.valueOf("100",16);
Integer i12 = Integer.valueOf("FF",16);
// Integer i13 = Integer.valueOf("FF"); // NumberFormatException발생
System.out.println(i);
System.out.println(i2);
System.out.println(i3);
System.out.println("100(2) -> "+i4);
System.out.println("100(8) -> "+i5);
System.out.println("100(16)-> "+i6);
System.out.println("FF(16) -> "+i7);
System.out.println("100(2) -> "+i9);
System.out.println("100(8) -> "+i10);
System.out.println("100(16)-> "+i11);
System.out.println("FF(16) -> "+i12);
}
}
💻 출력
100
100
100
100(2) -> 4
100(8) -> 64
100(16)-> 256
FF(16) -> 255
100(2) -> 4
100(8) -> 64
100(16)-> 256
FF(16) -> 255
이전에는 기본형과 참조형 간의 연산이 불가능했기 때문에, 래퍼 클래스로 기본형을 객체로 만들어서 연산해야 했다.
이제는 컴파일러가 알아서 형변환 해주는데
기본형 값을 래퍼 클래스 객체로 자동 변환해주는 것을 '오토박싱'
그 반대를 '언박싱' 이라고 한다.
<예제 9-16 >
✍️ 입력
class Ex9_16 {
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 lng = 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
[출처] 자바의 정석 <기초편> (남궁 성 지음)