본문 바로가기

Java/문법

10 - 6장 최상위 클래스 Object

- Object 클래스 

 - 자바의 모든 클래스는 Object 클래스를 상속받음 

 - 자바의 최상위 클래스 

 - 컴파일러는 아무런 클래스로 상속하지 않으면 자동으로 extends Object를 삽입해 Object 클래스를 상속 

 

class A {
 //class A extends Object 
 //상속을 하지 않을때 extends Object 자동으로 추가 
 
}

class B extends A {

}

- Object <- A <- B 의 관계 

- 자바의 모든 클래스는 어떤 객체로 만들든지 Object타입으로 선언 가능

Object oa = new A();
Object ob = new B();

 

 - System.out.println(Object X)

  -기본 자료형 이외에 Object를 입력매개변수로 하는 println()메서드를 오버로딩 

  - 사용자가 어떤 클래스 타입의 객체를 생성하더라도 다형성에 따라 Object타입이라고 불릴 수 있으므로 

  입력 매개변수로 모든 타입의 객체 받아들일 수 있음

 

1. Object 클래스의 주요 메서드 

 

 - 자바의 모든 클래스가 Object 클래스의 메서드를 포함 

 

1) toString() - 객체 정보를 문자열로 출력

 

 - 객체 정보 : '패키지명.클래스명@해시코드'

 - 해시코드 : 객체가 저장된 위치와 관련된 값

 - 실제 객체의 정보를 표현하고자 할 때는 대부분 클래스명이나 숫자로 나열된 해시코드보다는

 객체에 포함돼 있는 필드값 출력

- 자식 클래스에서는 toString() 메서드를 오버라이딩해 사용

 

class A {
	int a = 3;
    int b = 4;
}
A aa = new A();
System.out.print("%x\n", aa.hashcode()); //70dea4e3
System.out.println(aa); //aa.toString()이 자동으로 실행
                        //패키지.클래스명@해시코드

 - A 객체를 생성한 후 hashCode() 메서드의 리턴값을 16진수로 출력하면 aa 객체의 위칫값과 관련된 고유값 출력

 - println() 메서드는 객체를 출력하면 자동으로 객체 내의 toString() 메서드를 호출

   System.out.println(aa) == System.out.println(aa.toString()) 

 

class B { //toString overriding
	int a = 3;
    int b = 4;
    @override
    public String toString(0 {
    	return "필드값 : a" + a + "b:" +b;
    }
}

 - toString()의 출력 결과인 '패키지명.클래스명@해시코드'는 객체의 직관적인 정보를 제공하지 못함

 - 자식 클래스에서 toString() 메서드를 오버라이딩해 사용하는것이 일반적 

 

B bb = new B();
System.out.println(bb); // bb.toString()이 자동으로 실행
                        // 필드값 : a =3, b = 4

- 클래스 B에서 오버라이딩한 toString메서드에서는 자신의 필드 2개의 값을 출력하는 문자열을 리턴 

package ObjectMethod_toString;

class A { //extends Object(컴파일러에 따라 자동으로 추가)
	int a=3;
	int b=4;
}

class B {
	int a=3;
	int b=4;
	
	public String toString() {
		return "필드값(a,b) =" +a +" "+b;
	}
}
public class OB_toString {
	public static void main(String[] args) {
		//객체 생성
		A a = new A();
		B b = new B();
		
		//메서드 호출
		System.out.printf("%x\n", a.hashCode()); //hashcode를 16진수로 표현
		System.out.println(a.toString());
		System.out.println(b);
	}
}

 

 2) equals(Object obj) - 스택 메모리값 비교 

 

 - 입력매개변수로 넘어온 객체와 자기 객체의 스택 메모리 변숫값을 비교해 그 결과를 true / false로 리턴하는 메서드 

 - 객체의 스택메모리값 비교 

 - 실제 데이터값이 아닌 실제 데이터의 위치(번지)를 비교 

 - 등가 비교 연산(==)과 완벽하게 동일한 기능 수행

 

class A {
	String name;
    A(String name) {
    	this.name = name; 
    }
}

 -  필드 name 생성자를 이용해 초기화 

 

 A aa1 = new A("안녕");
 A aa2 = new A("안녕");
 System.out.println(aa1 == aa2); //false
 System.out.println(aa1.equals(aa2)); //false

 - 객체 내부의 값은 동일하지만, 실제 객체는 다른 곳에 위치하므로 위칫값을 나타내는 스택 메모리값은 서로 다름

class B { //equals() 메서드 overriding
	String name;
    B(String name) {
    	this.name = name;
    }
    @Overriding
    public boolean equals(Object obj) {
    	if(obj instance of B) {
        	if(this.name ==((B) obj).name)
            	return true;
         }
         return false;
    }
}

- 실제 내용을 비교하고자 할 때는 equals()메서드를 오버라이딩해 사용 

- equals()메서드를 오버라이딩

- 메서드 내부에서는 자신의 name값과 입력받은 객체의 name 값을 비교해 동일하면 ture, 동일하지 않으면 false

- 자신의 객체 타입을 일치시키기 위해 캐스팅을 할 수 있는지를 확인하는 instanceof 키워드와 다운캐스팅을 사용 

 

B bb1 = new B("안녕");
B bb2 = new B("안녕");
System.out.println(bb1 == bb2); //false
System.out.println(bb1.equals(bb2)); //true

 

3) hashCode() - 객체의 위치와 연관된값

 

 - 객체의 위치와 관련된 값, 실제 위치를 나타내는 값은 아님 

 - 객체의 위칫값을 기준으로 생성된 고윳값 

 -  두 객체의 내용을 비교하기 위해서는 equals() 메서드를 오버라이딩하는 것으로 충분

 -  Hash 형태의 자료구조에서는 동등 비교를 위해 hashCode() 결과값을 비교하므로

  equals() 메서드 오버라이딩 + hashcode() 오버라이딩 

 

 - HashMap 자료구조에서는 데이터를 (key, value)의 쌍으로 저장하며, key 값은 중복되지 않음 

 - 추가하고자 하는 데이터셋의 key가 기존의 key들과 동일한지를 확인하는 과정 필요 

 

 - HashMap에서의 두 객체 동일 조건 

  1. 두 객체의 hashcode()값 비교 

  2. 두 객체의 hashcode()값이 동일할때 equals() 메서드를 호출 

  3. equals() 메서드를 호출하면, 이 값이 true이면 같은 객체로 인식

 

 step 1. key1.hashCode() == key2.hashCode()

  - 두 key의 hashCode()값이 동일한지를 확인 

 step 2. key1.equals(key2) == true

  - equals() 메서드를 이용해 동일 여부를 확인 

 

 - step1, step2둘다 만족하면 같은 key 객체로 인식하므로 해당 데이터셋은 기존 데이터셋을 덮어씀

 - step1, step2 둘 중 하나라도 만족하지 않으면 다른 객체로 인식하므로 새로운 데이터셋으로 추가 

 

class A {
	String name;
    A(String name) {
    	this.name = name;
    }
    @override
    public boolean equals(Object obj) {
    	if(obj instanceof A) {
        	if(this.name == ((A)obj.name)
            	return ture;
        }
        return false;
    }
    @override
    public String toString() {
    	return name;
    }
}

 - 클래스 A 

  - equals() 메서드만 오버라이딩 

  - 비교대상의 객체와 name 필드값이 동일하면 true를 리턴 

 

class B {
	String name;
    A(String name) {
    	this.name = name;
    }
    @override
    public boolean equals(Object obj) {
    	if(obj instanceof A) {
        	if(this.name == ((A)obj.name)
            	return ture;
        }
        return false;
    }
    
    @override
    public int hashCode() {
    	return name.hashcode();
    }
    
    @override
    public String toString() {
    	return name;
    }
}

 - 클래스 B

  - equals() 메서드 + hashCode() 메서드  

  - String 클래스 내부의 hashCode() 메서드는 문자열마다 고유의 해시코드를 만들어 리턴해 주는 메서드 

  - 문자열이 동일하면, 동일한 해시코드 리턴

 

HashMap<Integer, String> hm1 = new HashMap<>();
hm1.put(1,"데이터1");
hm1.put(2,"데이터2");
hm1.put(3,"데이터3");
System.out.println(hm1); // {1=데이터2, 2= 데이터3}

 - 동일한 2개의 Key값이 중복돼 들어감

 - 최종적으로 저장된 데이터셋은 2개

 - key=1일때의 Value값은 나중에 들어간 "데이터2"

 

HashMap<A,String> hm2 = new HashMap<>();
hm2.put(new A("첫번째"), "데이터1");
hm2.put(new A("첫번째"), "데이터2");
hm2.put(new A("두번째"), "데이터3");
System.out.println(hm2); // {첫번째= 데이터1, 두번째=데이터2, 세번째=데이터3}

 - 클래스 A는 hashcode()를 오버라이딩 하지 않음 

 - 클래스 A 내부에서 사용할 수 있는 hashcode()는 Object의 hashCode()

 - object의 hashcode()는 객체의 위치에 따라 생성한 고윳값을 리턴

 - 두 객체가 서로 다른 위치에 생성 -> 두 객체의 hashcode()값도 서로 다름 

 

HashMap<B,String> hm3 = new HashMap<>();
hm3.put(new B("첫번째"),"데이터1");
hm3.put(new B("두번째"),"데이터2");
hm3.put(new B("세번째"),"데이터3");
System.out.println(hm3); // {첫번째=데이터2, 두번째=데이터3}

 - 클래스 B는 hashCode() 오버라이딩 

 - name.hashCode()를 사용해 문자열에 따라 해시코드값을 리턴 

 - 두 객체의 name 값이 같으므로 두 값의 hashCode()값도 같을 것임 

 - equals()도 true 리턴