class UnbelievableUserInfo {
// 이름은 null이 될 수 없음.
public String name = "홍길동";
// 계좌는 0보다 커야 함.
public int account = 10000;
// TODO: name 과 account에 부적절한 값이 할당되지 못하도록 처리하시오.
// name과 account 는 private으로 변경되어야 한다.
// END
}
public class UnbelievableTest {
public static void main(String[] args) {
UnbelievableUserInfo info = new UnbelievableUserInfo();
System.out.printf("사용자 정보:%s, %d%n", info.name, info.account);
info.name = null;
info.account = -1000;
System.out.printf("사용자 정보:%s, %d%n", info.name, info.account);
}
}
위 코드를 아래 처럼 변경하여 정보를 은닉 및 보호할 수 있다.
class UnbelievableUserInfo {
// 이름은 null이 될 수 없음.
private String name = "홍길동";
// 계좌는 0보다 커야 함.
private int account = 10000;
// TODO: name 과 account에 부적절한 값이 할당되지 못하도록 처리하시오.
// name과 account 는 private으로 변경되어야 한다.
public String getName() {
return name;
}
public void setName(String name) {
if(name == null) {
System.out.println("name is not null"); // 보호
return;
}
this.name = name;
}
public int getAccount() {
return account;
}
public void setAccount(int account) {
if(account < 0) {
System.out.println("account must bigger than zero");
return;
}
this.account = account;
}
// END
}
public class UnbelievableTest {
public static void main(String[] args) {
UnbelievableUserInfo info = new UnbelievableUserInfo();
System.out.printf("사용자 정보:%s, %d%n", info.getName(), info.getAccount());
info.setName(null);
info.setAccount(-1000);
System.out.printf("사용자 정보:%s, %d%n", info.getName(), info.getAccount());
}
}
객체의 생성을 제한해야 한다면?
Singleton 디자인 패턴
외부에서 생성자에 접근 금지 -> 생성자의 접근 제한자를 private으로 설정
내부에서는 private에 접근 가능하므로 직접 객체 생성 -> 멤버 변수이므로 private 설정
외부에서 private member에 접근 가능한 getter 생성 -> setter는 불필요
객체 없이 외부에서 접근할 수 있도록 getter와 변수에 static 추가
외부에서는 언제나 getter를 통해서 객체를 참조하므로 하나의 객체 재사용
class SingletonClass {
// TODO:SingletonClass에 Singleton Design Pattern을 적용하시오.
private SingletonClass() {
super();
}
private static SingletonClass instance = new SingletonClass();
public static SingletonClass getInstance() {
return instance;
}
// END
public void sayHello() {
System.out.println("Hello");
}
}
public class SingletonTest {
public static void main(String[] args) {
// TODO:SingletonClass를 사용해보세요.
SingletonClass sc1 = SingletonClass.getInstance();
SingletonClass sc2 = SingletonClass.getInstance();
//sc1 == sc2 이다.
sc1.sayHello();
// END
}
}
void beforePoly(){
Person [] persons = new Person[10];
persons[0] = new Person();
SpiderMan[] spiderMans = new Spider[10];
spiderMans[0] = new SpiderMan();
}
void afterPoly(){
Person[] persons = new Person[10];
persons[0] = new Person();
persons[1] = new SpiderMan();
}
위와 같이 Person을 상속하는 SpiderMan 클래스 또한 Class와 같이 배열로 묶을 수 있다.
Object는 모든 클래스의 조상
자바의 자료 구조를 간단하게 처리할 수 있음
public ArrayList(int initialCapacity){
if(initalCapacity > 0){
this.elemantData = new Object[initalCapacity];
}else if(initialCapacity == 0){
this.elementData = EMPTY_ELEMENTDATA;
} else{
throw new IllegalArgumentException("Illegal Capacity");
}
}
메모리에 있는 것과 사용할 수 있는 것의 차이
Person person = new SpiderMan();
person은 SpiderMan의 기능을 모른다.
메모리에 있더라고 참조하는 변수의 타입에 따라 접근할 수 있는 내용이 제한됨
Phone phone = new Phone();
Object obj = phone;
Phone phone = new SmartPhone();
SmartPhone sPhone = (SmartPhone)phone;
person person = new Person();
SpiderMan sman = (SpiderMan) person;
sman.fireWeb();
메모리 객체는 fireWeb이 없음
컴파일은 됨 -> 하지만 런타임 에러 발생
instanceof 연산자
Person person = new Person();
if(person instanceof SpideMan){
SpiderMan sman = (SpiderMan) person;
}
정적 바인딩
참조 변수의 타입
에 따라 연결이 달라짐멤버 변수가 중복
될 때 또는 static method동적 바인딩
다형성을 이용해서 메서드 호출이 발생할 때 runtime에 메모리의 실제 객체의 타입으로 결정
상속 단계에서 객체의 instance method가 재정의 되었을 때 마지막에 재정의 된 자식 클래스의 메서드가 호출됨
* 최대한 메모리에 생성된 실제 객체의 최적화 된 메서드가 동작한다.
public class Person{
...
@Override
public String toString() {
return "사람이름 : " + this.name;
}
}
public class PolyTest {
public static void main(String[] args) {
SpiderMan sman = new SpiderMan();
System.out.println(sman); // obj의 toString()이 출력됨 -> Person의 toString()이 출력됨
}
}
public class SpiderMan extends Person{
...
@Override
public String toString() {
return "SpiderMan [isSpider=" + isSpider + ", toString()=" + super.toString() + "]";
}
}
public class PolyTest {
public static void main(String[] args) {
SpiderMan sman = new SpiderMan();
System.out.println(sman); // obj의 toString()이 출력됨 -> Person의 toString()이 출력됨 -> SpiderMan의 toString()이 출력됨
}
}
용도에 따른 가장 적합한 메서드 구성은?
public class AppropriateParameter {
public void useJump1(Object obj) {
if (obj instanceof Person) {
Person casted = (Person) obj;
casted.jump();
}
}
public void useJump2(Person person) {
person.jump();
}
public void useJump3(SpiderMan spiderMan) {
spiderMan.jump();
}
public static void main(String[] args) {
Object obj = new Object();
Person person = new com.ssafy.day3.a_inheritance.Person();
SpiderMan sman = new com.ssafy.day3.a_inheritance.SpiderMan();
AppropriateParameter ap = new AppropriateParameter();
// TODO:ap의 useJumpX를 obj, person, sman으로 호출해보세요.
// 호출 가능하지만 실제로 jump할 수 없다.
ap.useJump1(obj);
ap.useJump1(person);
ap.useJump1(sman);
//ap.useJump1(obj);
ap.useJump1(person);
ap.useJump1(sman);
//ap.useJump1(obj);
//ap.useJump1(person);
ap.useJump1(sman);
// END
} // end of main
}//end of class
Object는 jump를 못하니까 Person까지 매서드 정의하는게 가장 적절하다.
public class Product extends Object{
private String sn;
private int age;
public Product(String sn) {
this.sn = sn;
}
@Override
public String toString() {
return "Product [sn=" + sn + "]";
}
// TODO: toString, equals, hashCode를 적절히 재정의해보자.
@Override
public boolean equals(Object obj) {
if(obj instanceof Product) {
Product p = (Product) obj;
return this.sn.equals(p.sn);
}
return false;
}
@Override
public int hashCode() {
//return Integer.valueOf(sn);
//Wrapper class, String class, Object class의 HashCode()를 활용해서 구현한다.
return (sn+age).hashCode(); //이런식으로 구현 가능
}
// END
}