item 88 junghyunlyoo - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki
๊นจ์ง๊ธฐ ์ฌ์ด ์ง๋ ฌํ์์์ ๋ถ๋ณ์
item 50์์๋ ๋ถ๋ณ์ธ ๋ ์ง ๋ฒ์ ํด๋์ค๋ฅผ ๋ง๋๋ ๋ฐ ๊ฐ๋ณ Date ํ๋๋ฅผ ์ด์ฉํ๋ค.
๊ทธ๋์ ๋ถ๋ณ์์ ์งํค๊ณ ๋ถ๋ณ์ ์ ์งํ๊ธฐ ์ํด ์์ฑ์์ ์ ๊ทผ์์์ Date ๊ฐ์ฒด๋ฅผ ๋ฐฉ์ด์ ์ผ๋ก ๋ณต์ฌํ๋๋ผ ์ฝ๋๊ฐ ์๋นํ ๊ธธ์ด์ก๋ค.
์๋๊ฐ ๋ฐ๋ก ๊ทธ ํด๋์ค๋ค.
public final class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if (this.start.compareTo(this.end) > 0)
throw new IllegalArgumentException(this.start + "๊ฐ " + this.end + "๋ณด๋ค ๋ฆ๋ค.");
}
public Date start() {
return new Date(start.getTime());
}
public Date end() {
return new Date(end.getTime());
}
}
Period ๊ฐ์ฒด์ ๋ฌผ๋ฆฌ์ ํํ์ด ๋ ผ๋ฆฌ์ ํํ๊ณผ ๋ถํฉํ๋ฏ๋ก, ์ด ํด๋์ค๋ฅผ ์ง๋ ฌํํ๊ธฐ ์ํด์ Serializable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ธฐ๋ง ํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
ํ์ง๋ง ๊ทธ๋ฌ๋ฉด ์ด ํด๋์ค์ ์ค์ํ ๋ถ๋ณ์์ ๋๋ ๋ณด์ฅํ์ง ๋ชปํ๊ฒ ๋๋ค.
์์ธ์ ๋ฐ๋ก readObject ๋ฉ์๋์ ์๋ค. readObject ๋ฉ์๋๋ ์ค์ง์ ์ผ๋ก ๋ ๋ค๋ฅธ public ์์ฑ์๋ผ๊ณ ํ ์ ์๋ค.
๋ฐ๋ผ์ ์์ฑ์๊ฐ ์ํํ๋ ์กฐ๊ฑด๋ค์ readObject์๋ ๋๊ฐ์ด ์ํํด์ผ ํ๋ค. (item 50)
๊ทธ๋ ์ง ์์ผ๋ฉด ๊ณต๊ฒฉ์๋ ์์ฃผ ์์ฝ๊ฒ ํด๋น ํด๋์ค์ ๋ถ๋ณ์์ ๊นจ๋จ๋ฆด ์ ์๋ค.
์ง๋ ฌํ์์์ ๋ถ๋ณ์ ๋ณด์
readObject๋ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ดํธ ์คํธ๋ฆผ์ ๋ฐ๋ ์์ฑ์๋ผ ํ ์ ์๋ค. ๋ฐ์ดํธ ์คํธ๋ฆผ์ ๋ณดํต ์ ์์ ์ผ๋ก ์์ฑ๋ ์ธ์คํด์ค๋ฅผ ์ง๋ ฌํํด์ ๋ง๋ค์ด์ง๋ค.
๊ทธ๋ฐ๋ฐ ์ด ๋ฐ์ดํธ ์คํธ๋ฆผ์ ์๋์ ์ผ๋ก ์์ ํ๊ฑฐ๋ ์์ฑํ์ฌ readObect์ ๊ฑด๋ค๋ฉด ๋ฌธ์ ๊ฐ ์๊ธฐ๊ฒ ๋๋ค.
์ ์์ ์ธ ์์ฑ์๋ก๋ ๋ง๋ค ์ ์๋ ๊ฐ์ฒด๊ฐ ์์ฑ๋๊ธฐ ๋๋ฌธ์ด๋ค.
์ด ๋ฌธ์ ๋ฅผ ๊ณ ์น๋ ค๋ฉด readObject ๋ฉ์๋๊ฐ defaultReadObject๋ฅผ ํธ์ถํ ๋ค์ ์ญ์ง๋ ฌํ๋ ๊ฐ์ฒด๊ฐ ์ ํจํ์ง ๊ฒ์ฌํด์ผ ํ๋ค.
์ด ์ ํจ์ฑ ๊ฒ์ฌ์ ์คํจํ๋ฉด InvalidObjectException์ ๋์ง๊ฒ ํ์ฌ ์๋ชป๋ ์ญ์ง๋ ฌํ๊ฐ ์ผ์ด๋๋ ๊ฒ์ ๋ง์ ์ ์๋ค.
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
// ๋ถ๋ณ์์ ๋ง์กฑํ๋์ง ๊ฒ์ฌํ๋ค.
if(start.compareTo(end) > 0) {
throw new InvalidObjectException(start + "๊ฐ " + end + "๋ณด๋ค ๋ฆ๋ค.");
}
}
ํ์ง๋ง ์์ง ๋ฌธ์ ๊ฐ ์๋ค. ์ ์์ ์ผ๋ก ์ง๋ ฌํ๋ Period ์ธ์คํด์ค์ ๋ฐ์ดํธ ์คํธ๋ฆผ ๋์ private Date ํ๋๋ก์ ์ฐธ์กฐ๋ฅผ ์ถ๊ฐํ๋ฉด ๊ฐ๋ณ Period ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด ๋ผ ์๊ฐ ์๋ค.
๊ณต๊ฒฉ์๋ ObjectInputStream์์ Period ์ธ์คํด์ค๋ฅผ ์ฝ์ ํ, ์คํธ๋ฆผ ๋์ ์ถ๊ฐ๋์ด ์๋ '์ ์์ ์ธ ๊ฐ์ฒด ์ฐธ์กฐ'๋ฅผ ์ฝ์ด Period ๊ฐ์ฒด์ ๋ด๋ถ ์ ๋ณด๋ฅผ ์ป์ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ด ์ฐธ์กฐ๋ก ์ป์ Date ์ธ์คํด์ค๋ค์ ๊ฒ์ฌ ์์ด ์์ ํด๋ฒ๋ฆด ์๋ ์์ผ๋, Period ์ธ์คํด์ค์ ํ๋๋ ๋ ์ด์ ๊ฒ์ฌ๋์ง ์๋๋ค.
๋ค์์ ์ด ๊ณต๊ฒฉ์ด ์ด๋ป๊ฒ ์ด๋ค์ง๋์ง ๋ณด์ฌ์ฃผ๋ ์๋ค.
public class MutablePeriod {
//Period ์ธ์คํด์ค
public final Period period;
//์์ ์๊ฐ ํ๋ - ์ธ๋ถ์์ ์ ๊ทผํ ์ ์์ด์ผ ํ๋ค.
public final Date start;
//์ข
๋ฃ ์๊ฐ ํ๋ - ์ธ๋ถ์์ ์ ๊ทผํ ์ ์์ด์ผ ํ๋ค.
public final Date end;
public MutablePeriod() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectArrayOutputStream out = new ObjectArrayOutputStream(bos);
//์ ํจํ Period ์ธ์คํด์ค๋ฅผ ์ง๋ ฌํํ๋ค.
out.writeObject(new Period(new Date(), new Date()));
/**
* ์
์์ ์ธ '์ด์ ๊ฐ์ฒด ์ฐธ์กฐ', ์ฆ ๋ด๋ถ Date ํ๋๋ก์ ์ฐธ์กฐ๋ฅผ ์ถ๊ฐํ๋ค.
* ์์ธ ๋ด์ฉ์ ์๋ฐ ๊ฐ์ฒด ์ง๋ ฌํ ๋ช
์ธ์ 6.4์ ์ ์ฐธ๊ณ
*/
byte[] ref = {0x71, 0, 0x7e, 0, 5}; // ์ฐธ์กฐ #5
bos.write(ref); // ์์ start ํ๋ ์ฐธ์กฐ ์ถ๊ฐ
ref[4] = 4; //์ฐธ์กฐ #4
bos.write(ref); // ์ข
๋ฃ(end) ํ๋ ์ฐธ์กฐ ์ถ๊ฐ
// Period ์ญ์ง๋ ฌํ ํ Date ์ฐธ์กฐ๋ฅผ ํ์น๋ค.
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
period = (Period) in.readObject();
start = (Date) in.readObject();
end = (Date) in.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new AssertionError(e);
}
}
}
๋ค์ ์ฝ๋๋ฅผ ์คํํ๋ฉด ์ด ๊ณต๊ฒฉ์ด ์ค์ ๋ก ์ด๋ค์ง๋ ๋ชจ์ต์ ํ์ธํ ์ ์๋ค.
public static void main(String[] args) {
MutablePeriod mp = new MutablePeriod();
Period p = mp.period;
Date pEnd = mp.end;
//์๊ฐ ๋๋๋ฆฌ๊ธฐ
pEnd.setYear(78);
System.out.println(p); // Wed Nov 22 00:21:29 PST 2017 - Wed Nov 22 00:21:29 PST 1978
//60๋
๋๋ก ํ๊ท
pEnd.setYear(60);
System.out.println(p); // Wed Nov 22 00:21:29 PST 2017 - Wed Nov 22 00:21:29 PST 1969
}
์ด ์์์ Period ์ธ์คํด์ค๋ ๋ถ๋ณ์์ ์ ์งํ ์ฑ ์์ฑ๋์ง๋ง, ์ด๋ ๊ฒ ์๋์ ์ผ๋ก ๋ด๋ถ์ ๊ฐ์ ์์ ํ ์ ์๋ค.
์ด์ฒ๋ผ ๋ณ๊ฒฝํ ์ ์๋ Period ์ธ์คํด์ค๋ฅผ ํ๋ํ ๊ณต๊ฒฉ์๋ ์ด ์ธ์คํด์ค๊ฐ ๋ถ๋ณ์ด๋ผ๊ณ ๊ฐ์ ํ๋ ํด๋์ค์ ๋๊ฒจ ์์ฒญ๋ ๋ณด์ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์๋ค.
์ด๋ฌํ ๋ฌธ์ ์ ๊ทผ์์ Period์ readObject()๊ฐ ๋ฐฉ์ด์ ๋ณต์ฌ๋ฅผ ์ถฉ๋ถํ ํ์ง ์์ ๋ฐ ์๋ค.
๊ฐ์ฒด๋ฅผ ์ญ์ง๋ ฌํํ ๋๋ ํด๋ผ์ด์ธํธ๊ฐ ์์ ํด์๋ ์ ๋๋, ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ๊ฐ๋ ํ๋๋ฅผ ๋ชจ๋ ๋ฐ๋์ ๋ฐฉ์ด์ ์ผ๋ก ๋ณต์ฌํด์ผ ํ๋ค.
๋ฐ๋ผ์ readObject์์๋ ๋ถ๋ณ ํด๋์ค ์์ ๋ชจ๋ private ๊ฐ๋ณ ์์๋ฅผ ๋ฐฉ์ด์ ์ผ๋ก ๋ณต์ฌํด์ผ ํ๋ค.
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
// ๊ฐ๋ณ ์์๋ค์ ๋ฐฉ์ด์ ์ผ๋ก ๋ณต์ฌํ๋ค.
start = new Date(start.getTime());
end = new Date(end.getTime());
// ๋ถ๋ณ์์ ๋ง์กฑํ๋์ง ๊ฒ์ฌํ๋ค.
if(start.compareTo(end) > 0) {
throw new InvalidObjectException(start + "๊ฐ " + end + "๋ณด๋ค ๋ฆ๋ค.");
}
}
๋ฐฉ์ด์ ๋ณต์ฌ๋ฅผ ์ ํจ์ฑ ๊ฒ์ฌ๋ณด๋ค ์์ ์ํํ๋ค.
๋ง์ฝ ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ๋ฐฉ์ด์ ๋ณต์ฌ๋ณด๋ค ์์ ์๋ค๋ฉด, ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํต๊ณผํ ํ ๋ฐฉ์ด์ ์ผ๋ก ๋ณต์ฌํ๊ธฐ ์ ์ ๊ณต๊ฒฉ์๊ฐ ์ฐธ์กฐ๋ฅผ ํตํด์
Date๊ฐ์ ๋ฐ๊ฟ๋ฒ๋ฆฌ๊ณ ๊ทธ ํ์ ๋ฐฉ์ด์ ์ผ๋ก ๋ณต์ฌํ๊ฒ ๋๊ธฐ ๋๋ฌธ์ธ ๋ฏ ํ๋ค.
ํํธ final ํ๋๋ ๋ฐฉ์ด์ ๋ณต์ฌ๊ฐ ๋ถ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์, start์ end ํ๋์์ final ํ์ ์๋ฅผ ์ ๊ฑฐํด์ผ ํ๋ค.
๊ธฐ๋ณธ readObject๋ฅผ ์ฌ์ฉํด๋ ๋๋ ๊ฒฝ์ฐ
transient ํ๋๋ฅผ ์ ์ธํ ๋ชจ๋ ํ๋์ ๊ฐ์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์, ์ ํจ์ฑ ๊ฒ์ฌ ์์ด ํ๋์ ๋์ ํ๋ public ์์ฑ์๋ฅผ ์ถ๊ฐํด๋ ๊ด์ฐฎ์๊ฐ?
๊ทธ๋ ๋ค๋ฉด ๊ธฐ๋ณธ readObject๋ฅผ ์ฌ์ฉํด๋ ๋๋ค.
ํ์ง๋ง ๊ทธ๋ ์ง ์๋ค๋ฉด ์ปค์คํ readObject()๋ฅผ ๋ง๋ค์ด ์์ฑ์์์์ ์ ํจ์ฑ ๊ฒ์ฌ์ ๋์ผํ ์์ค์ ๊ฒ์ฌ๋ฅผ ํด์ผ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฐฉ์ด์ ๋ณต์ฌ๋ ํ์์ด๋ค.
ํน์ ์ง๋ ฌํ ํ๋ก์ ํจํด(item 90)์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์๋ค. (์ด ํจํด์ ์ญ์ง๋ ฌํ๋ฅผ ์์ ํ๊ฒ ๋ง๋๋ ๋ฐ ํ์ํ ๋ ธ๋ ฅ์ ์๋นํ ๊ฒฝ๊ฐํด์ฃผ๋ฏ๋ก ์ ๊ทน ๊ถ์ฅ๋๋ค)