- 1_생성자12345678910111213141516171819202122232425262728293031
package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
// 객체를 생성하는 방법.
// 1. 생성자
// a. 생성자 메소드의 이름은 클래스의 이름과 같다.
// b. 생성자의 오버로딩은 한계가 있다.
class
RandomIntGenerator {
final
int
min;
final
int
max;
public
RandomIntGenerator(
int
min,
int
max) {
this
.min = min;
this
.max = max;
}
public
RandomIntGenerator(
int
min) {
this
.min = min;
this
.max = Integer.MAX_VALUE;
}
// 1. 동일한 인자를 받는 다른 형태의 객체 생성 방법을 제공할 수 없다.
// public RandomIntGenerator(int max) {}
}
public
class
Example1 {
public
static
void
main(String[] args) {
// 2. 객체를 생성하는 코드를 통해서 어떤 객체가 생성되는지를 절대 알 수 없다.
RandomIntGenerator obj1 =
new
RandomIntGenerator(
0
,
100
);
RandomIntGenerator obj2 =
new
RandomIntGenerator(
0
);
}
}
- 2_정적 팩토리 메소드(static factory method)
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
// 2. 객체를 생성하는 방법 2.
// : 정적 팩토리 메소드(static factory method)
// 정의 : 객체를 생성할 때 생성자가 아닌 정적 메소드를 통해 생성하자.
// 단점
// 1. 상속을 통한 기능 변경이 불가능하다. (테스트가 힘들다)
// 2. 이름을 잘 지어야 한다.
class
RandomIntGenerator {
final
int
min;
final
int
max;
// private 이므로 외부에서 접근이 불가능하다.
private
RandomIntGenerator(
int
min,
int
max) {
this
.min = min;
this
.max = max;
}
// 객체를 생성하는 다양한 정적 메소드를 제공하자.
// 장점 1. 정적 팩토리 메소드는 결국 메소드 이므로 이름에 제한이 없다.
public
static
RandomIntGenerator between(
int
min,
int
max) {
return
new
RandomIntGenerator(min, max);
}
public
static
RandomIntGenerator biggerThan(
int
min) {
return
new
RandomIntGenerator(min, Integer.MAX_VALUE);
}
public
static
RandomIntGenerator smallerThan(
int
max) {
return
new
RandomIntGenerator(Integer.MIN_VALUE, max);
}
// 장점 3. 생성자처럼 매번 생성할 필요가 없다.
// Integer.valueOf(3), Long.valueOf(3)
private
static
final
RandomIntGenerator INSTANCE
=
new
RandomIntGenerator(Integer.MIN_VALUE, Integer.MAX_VALUE);
public
static
RandomIntGenerator getInstance() {
return
INSTANCE;
}
}
public
class
Example2 {
public
static
void
main(String[] args) {
// 장점 2. 라이브러리 사용자들은 객체가 어떤 정책을 가지고 생성되는지
// 이름을 통해 쉽게 이해할 수 있다.
RandomIntGenerator obj1 = RandomIntGenerator.between(
0
,
100
);
RandomIntGenerator obj2 = RandomIntGenerator.smallerThan(
100
);
RandomIntGenerator obj3 = RandomIntGenerator.biggerThan(
0
);
}
}
3_불필요한 객체 생성
: Junit test 결과 testLong2() 는 오토박싱을 통해 객체를 계속 생성한다. -> testLong()에 비해 현저히 느리다.12345678910111213141516171819202122232425262728293031323334353637package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
import
org.junit.Test;
public
class
Example3 {
public
static
void
main(String[] args) {
// 불필요한 객체가 생성된다.
String s1 =
new
String(
"hello"
);
String s2 =
"hello"
;
// 같은 가상 머신에서 실행되는 모든 코드가 해당 객체를 재사용한다.
// 불변 객체 ( Immutable object ): 불변객체는 생성 후에는 변하지 않는 object 로, 언제든 재사용이 가능
// 불변객체의 불필요한 객체 생성을 막으려면 생성자보다는 static 팩토리 메소드를 사용하는 것이 좋습니다. ( Factory 에서 관리 )
Boolean b1 =
new
Boolean(
true
);
Boolean b2 = Boolean.TRUE;
}
@Test
public
void
testLong2() {
Long sum = 0L;
for
(
long
i =
0
; i < Integer.MAX_VALUE; ++i)
sum += i;
System.out.println(sum);
}
@Test
public
void
testLong() {
long
sum = 0L;
for
(
long
i =
0
; i < Integer.MAX_VALUE; ++i)
sum += i;
System.out.println(sum);
}
}
4_싱글톤(Singleton)
123456789101112131415161718192021222324252627282930313233343536373839404142434445package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
// 싱글톤(Singleton)
// 개념 : 하나의 인스턴스만 생성되는 클래스
// 장점 : 어디에서든 동일한 방법으로 동일한 객체를 얻을 수 있다.
// 단점 : 테스트 용이성 낮다.
// 객체와 객체간의 결합도가 높아진다.
// 자바에서 가장 많이 사용하는 싱글톤의 형태
// -> 자바 5 이상에서는 스레드 안전성도 보장된다.
class
Cursor {
// 방법 1. private 생성자
private
Cursor() {
}
// 방법 2.
// public static final Cursor INSTANCE1 = new Cursor();
// private static final Cursor INSTANCE = new Cursor();
private
static
final
ThreadLocal<Cursor> INSTANCE
=
new
ThreadLocal<Cursor>() {
@Override
protected
Cursor initialValue() {
return
new
Cursor();
}
};
public
static
Cursor getInstance() {
return
INSTANCE.get();
}
}
public
class
Example4 {
public
static
void
main(String[] args) {
System.out.println(Cursor.getInstance());
new
Thread(
new
Runnable() {
@Override
public
void
run() {
System.out.println(Cursor.getInstance());
}
}).start();
}
}
- 5_enum을 이용한 싱글톤
: Serialize()는 객체를 쓰고, 읽어도 하나의 객체로 유지된다.
: Refelction()는 객체가 생성되지 않는다.12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.ObjectInput;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutputStream;
import
java.io.Serializable;
import
java.lang.reflect.Constructor;
import
org.junit.Test;
// 아래의 싱글톤은 두 가지 경우에 객체가 두개이상 생성될 수 있습니다.
// 1. Reflection
// 2. 직렬화
//class Cursor implements Serializable {
// private Cursor() {
// }
//
// private Object readResolve() {
// return INSTANCE;
// }
//
// private static final Cursor INSTANCE = new Cursor();
//
// public static Cursor getInstance() {
// return INSTANCE;
// }
//}
// 위의 문제점을 해결하기 위해 새롭게 제안된 싱글톤
// 원소가 하나뿐인 enum 을 이용하자.
// 리플렉션을 통해 생성하는 것도 하는 것 불가능하고
// 직렬화에 의한 객체 생성도 자동적으로 처리해준다.
enum
Cursor {
INSTANCE;
public
static
Cursor getInstance() {
return
INSTANCE;
}
}
public
class
Example5 {
@Test
public
void
Serialize()
throws
Exception {
String filename =
"cursor.dat"
;
FileOutputStream fos =
new
FileOutputStream(filename);
ObjectOutputStream oos =
new
ObjectOutputStream(fos);
oos.writeObject(Cursor.getInstance());
fos.close();
oos.close();
FileInputStream fis =
new
FileInputStream(filename);
ObjectInputStream ois =
new
ObjectInputStream(fis);
Cursor c = (Cursor) ois.readObject();
System.out.println(c);
System.out.println(Cursor.getInstance());
}
@Test
public
void
Refelction()
throws
Exception {
Constructor<?> con = Cursor.
class
.getDeclaredConstructors()[
0
];
con.setAccessible(
true
);
Cursor c = (Cursor) con.newInstance();
System.out.println(c);
System.out.println(Cursor.getInstance());
}
}
6_지연된 초기화
1234567891011121314151617181920212223242526package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
// 자바의 싱글톤은 객체가 클래스 로더에 의해 로딩될 때 생성된다.
// : 객체의 생성 비용이 크거나, 객체를 사용하지 않을 경우 리소스 낭비가 심하다.
// (싱글톤은 가비지 컬렉션의 대상이 아니다.)
// 지연된 초기화의 필요성
// 1. 메모리 낭비 제거
// 2. 애플리케이션의 로딩 속도 개선
class
Cursor {
public
static
final
Cursor INSTANCE =
new
Cursor();
private
Cursor() {
System.out.println(
"Cursor created!"
);
}
public
static
void
foo() {
System.out.println(
"foo"
);
}
}
public
class
Example6 {
public
static
void
main(String[] args) {
Cursor.foo();
}
}
7_지연된 초기화2
: DCLP(Double Checked Locking Pattern)
: IODH(Initialization On Demand Holder) - 클래스가 최초로 초기화 되는 시점12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
class
Cursor {
// private static Cursor instance = null;
private
Cursor() {
System.out.println(
"Cursor"
);
};
// public static Cursor getInstance() {
// if (instance == null)
// instance = new Cursor();
//
// return instance;
// }
// 위의 지연된 초기화는 스레드 안전성이 없습니다.
// 해결해야 합니다.
// 1. 엄격한 동기화
// public synchronized static Cursor getInstance() {
// if (instance == null)
// instance = new Cursor();
//
// return instance;
// }
// 2. 위의 코드는 계속 동기화를 수행하기 때문에 느립니다.
// 생성 시점에만 동기화를 적용하고, 생성 이후에는 동기화를 제거하는 방법.
// DCLP(Double Checked Locking Pattern)
// public static Cursor getInstance() {
// if (instance == null) {
// synchronized (Cursor.class) {
// if (instance == null)
// instance = new Cursor();
// }
// }
// return instance;
// }
// 3. IODH(Initialization On Demand Holder)
// 클래스가 최초로 초기화 되는 시점
// 1) 클래스 T의 인스턴스가 생성될 때
// 2) 클래스 T의 정적 메소드가 호출되었을 때
// 3) 클래스 T의 정적 필드에 값이 할당 되었을 때 *
// 4) 클래스 T의 정적 필드가 상수 필드가 아니고 사용되었을 때 *
private
static
class
Singleton {
private
static
final
Cursor INSTANCE =
new
Cursor();
}
public
static
Cursor getInstance() {
return
Singleton.INSTANCE;
}
public
static
void
foo() {
System.out.println(
"foo"
);
}
}
public
class
Example7 {
public
static
void
main(String[] args) {
Cursor.foo();
Cursor.getInstance();
}
}
8_정적필드 초기화
123456789101112131415161718192021222324252627package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
import
java.util.Calendar;
// 정적 필드는 위에서부터 아래로 차근차근 초기화 합니다.
// 사용하기 전에 초기화 할 수 있도록 앞에 배치해야 합니다.
class
Person {
public
static
final
Person INSTANCE =
new
Person();
private
final
int
weight;
public
static
final
int
CURRENT_YEAR = Calendar.getInstance().get(Calendar.YEAR);
private
Person() {
weight = CURRENT_YEAR -
1984
;
}
public
int
weight() {
return
weight;
}
}
public
class
Example8 {
public
static
void
main(String[] args) {
System.out.println(Person.INSTANCE.weight());
System.out.println(Person.INSTANCE.CURRENT_YEAR);
}
}
9_정적필드 초기화2
: isTeenager()는 메소드 호출 당 Calendar, TimeZone, Date 2개의 객체가 생성된다.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
import
java.util.Calendar;
import
java.util.Date;
import
java.util.TimeZone;
import
org.junit.Test;
class
Person {
private
final
Date birthDate;
public
Person(Date b) {
this
.birthDate = b;
}
// 아래 메소드가 호출될 때마다 생성되는 객체는 다음과 같다.
// 1. Calendar
// 2. TimeZone
// 3. Date 2개
public
boolean
isTeenager() {
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone(
"GMT"
));
gmtCal.set(
1996
, Calendar.JANUARY,
1
,
0
,
0
,
0
);
Date start = gmtCal.getTime();
gmtCal.set(
2005
, Calendar.JANUARY,
1
,
0
,
0
,
0
);
Date end = gmtCal.getTime();
return
birthDate.compareTo(start) >=
0
&& birthDate.compareTo(end) <
0
;
}
private
static
final
Date START;
private
static
final
Date END;
static
{
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone(
"GMT"
));
gmtCal.set(
1996
, Calendar.JANUARY,
1
,
0
,
0
,
0
);
START = gmtCal.getTime();
gmtCal.set(
2005
, Calendar.JANUARY,
1
,
0
,
0
,
0
);
END = gmtCal.getTime();
}
public
boolean
isTeenager2() {
return
birthDate.compareTo(START) >=
0
&& birthDate.compareTo(END) <
0
;
}
}
public
class
Example9 {
@Test
public
void
test_isTeen() {
Calendar c = Calendar.getInstance();
c.set(
1995
, Calendar.MAY,
5
);
Person p =
new
Person(c.getTime());
for
(
int
i =
0
; i <
100000000
; ++i) {
p.isTeenager();
}
}
@Test
public
void
test_isTeen2() {
Calendar c = Calendar.getInstance();
c.set(
1995
, Calendar.MAY,
5
);
Person p =
new
Person(c.getTime());
for
(
int
i =
0
; i <
100000000
; ++i) {
p.isTeenager2();
}
}
}
10_StringBuilder
: + 연산자에 대한 오버로딩은 문자열만 제공.
: 매번 String 인스턴스를 생성하는 방식이라 성능 이슈 발생. 이를 개선하기 위해 JDK 1.5 버전 이후에는 컴파일 단계에서 StringBuilder로 컴파일 되도록 변경되었다. 그래서 JDK 1.5 이후부터는 +를 활용해도 성능상에 큰 이슈는 없다.1234567891011121314151617181920212223package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
// + 연산자에 대한 오버로딩은 문자열만 제공하고 있다.
// : '+' 연산자는 피연산자로 문자열이 있을 때만 문자열 연결 연산을 수행합니다.
// 문자열이 없으면 그냥 덧셈을 수행합니다.
public
class
Example10 {
public
static
void
main(String[] args) {
System.out.print(
"H"
+
"A"
);
// System.out.print('H' + 'A');
// 문자를 연결하는 방법 1.
StringBuilder sb =
new
StringBuilder();
sb.append(
'H'
).append(
'A'
);
System.out.print(sb);
// 방법 2.
System.out.println(
""
+
'H'
+
'A'
);
// 방법 3.
System.out.printf(
"%c%c"
,
'H'
,
'A'
);
}
}
11_부호확장, 제로 확장
: 부호확장은 상위 비트를 부호에 따라 0이나 으로 채우는 것
: 제로확장은 상위 비트를 0으로 채우는 것12345678910111213141516171819202122232425package
kr.co.ioacademy;
//iocademy 윤찬식 강사님
// int, long, byte : signed
// char : unsigned
// 변환되는 자료형에 상관없이 원래 부호가 있는 타입이면 부호 확장이 일어나고,
// char 자료형이면 0의 확장이 일어난다.
public
class
Example11 {
public
static
void
main(String[] args) {
System.out.println((
int
)(
char
)(
byte
)-
1
);
// int(4byte) -> byte(1byte) -> char(2byte) -> int(4byte)
// -1 -> (byte) -1
// ff ff ff ff -> ff
// -> (char)(byte) -1
// 부호 확장
// ff -> ff ff
// -> (int)(char)(byte) -1
// 제로 확장
// ff ff -> 00 00 ff ff
}
}
'Programing > Java' 카테고리의 다른 글
Effective Java - 객체 소멸 (0) | 2016.03.04 |
---|---|
Junit 사용하기 (0) | 2016.03.03 |
Java Reference와 GC (0) | 2016.02.29 |
Effective Java - Reference (0) | 2016.02.29 |
Garbage Collection (0) | 2015.11.15 |