반응형

-lang 패키지에 있는 모든 메서드들은 내가 import 하지 않아도 사용이 가능하다.  (워낙 많이 사용하니 기본적으로 import가 되어 있기 때문이다)

Date  day = new Date();

 

-여기서 Date 메서드는 Util 패키지에 있기 때문에, import 해서 써야한다.

 

 

## Object 클래스

 


-클래스를 선언할 때 extends 키워드로 다른 클래스를 상속하지 않으면 
 암시적으로 java.lang.Object 클래스를 상속하게 된다. 
 따라서 자바의 모든 클래스는 Object 클래스의 자식이거나 자손 클래스이다.

-Object는 자바의 최상위 부모 클래스에 해당한다.


# 객체 비교(equals())

다음은 Object의 equals() 메소드이다.

public boolean equals(Object obj)  {
...
}

 

 



equals() 메소드의 매개 타입은 Object인데, 이것은 모든 객체가 매개값으로 대입될 수 있음을 말 한다. 
그 이유는 Object가 최상위 타입이므로 모든 객체는 Object 타입으로 자동 타입 변환될 수 있기 때문이다.
Object 클래스의 equals() 메소드는 비교 연산자인 과 통일한 결과를 리턴한다. 
두 객체가 통일한 객체라면 true를 리턴하고 그렇지 않으면 false를 리턴한다.

 

 

 

#String equals와 Object equals의 차이

 

-이 아래에 문구는 false가 나오게 되는데 이는 String이 Object로 부터 상속받은 메서드를 Override 해서 재사용했기 때문이다. 그래서 기존에, Object의 equals는 == 과 같은 의미이지만, String의 equals 의미는 ==이 아닌, 정말 문자 그대로가 동일한지를 보는 것이다.

 

Object equals : 값을 비교하는 것이 아닌 객체가 같은지 비교

String equals : 값이 같은지 비교

 

 


package chapter10.example01;

public class Member {

	String id;
	
	public Member(String id) {
		this.id = id;
	}
	
	@Override
	// 여기서 Object 타입은 Object 뿐만 아니라, 상속되어 있는 객체 모두가 들어올 수 있다.
	public boolean equals(Object obj) {
		if(obj instanceof Member) {
			Member member = (Member) obj;
			if(id.equals(member.id)) {
				return true;
			}
		}
		return false;
	}
}
Member mem1 = new Member("blue");
Member mem2 = new Member("blue");
Member mem3 = new Member("yellow");

if(mem1 == mem2) {
    System.out.println("mem1과 mem2는 같은 객체입니다.");
} else {
    System.out.println("mem1과 mem2는 다른 객체입니다.");
}

if(mem1.equals(mem2)) {
    System.out.println("mem1과 mem2는 같은 객체입니다.");
} else {
    System.out.println("mem1과 mem2는 다른 객체입니다.");
}

if(mem2.equals(mem3)) {
    System.out.println("mem1과 mem3는 같은 객체입니다.");
} else {
    System.out.println("mem1과 mem3는 다른 객체입니다.");
}


 

 

# 객체 문자 정보(toString())

- 객체 == iv집합 이므로, 객체를 문자열로 변환한다는 것은 iv의 값을 문자열로 변환한다는 것과 같다.

- iv를 문자열로 변환하기 위해서 Object 클래스의 toString() 메서드를 오버라이딩 해준다.

 

 

- toString과 equals(), hashCode()에 대해서 정말 쉽게 이해할 수 있게 정리해놓았다.



-Object 클래스의 toString() 메소드는 객체의 문자 정보를 리턴한다. 
 (객체의 문자 정보란 객체를 문자열로 표현한 값을 말한다.)
 
-기본적으로 Object 클래스의 toString() 메소드는 "클래스명@16진수해시코드"로 구성된 문자 정보를 리턴한다.

Object obj = new Object();
System.out.println(obj.toString());

 

-obj 변수를 하나만 넣으면 toString이 자동으로 들어간다.

-to.String은 각 클래스마다 원하는 값을 가져올 수 있도록 오버라이딩(메서드 재정의) 해놨다.


-Object의 toString() 메소드의 리턴값은 자바 애플리케이션에서는 별 값어치가 없는 정보이므로 
  Object 하위 클래스는 toString() 메소드를 재정의(오버라이딩 )하여 간결하고 유익한 정보를 리턴 하도록 되어 있다. 

-예를 들어 java.util 패키지의 Date 클래스는 toString() 메소드를 재정의하여 
 현재 시스템의 날짜와 시간 정보를 리턴한다. 
 그리고 String 클래스는 toString() 메소드를 재정의해서 저장하고 있는 문자열을 리턴한다.

 

 


 

더보기

 

package chapter10.example02;

public class SmartPhone {

	String company;
	String os;
	
	public SmartPhone (String company, String os) {
		this.company = company;
		this.os = os;
	}
	
	@Override
	public String toString() {
		return company + ", " + os;
		
	}
}
package chapter10.example02;

public class SmartPhoneMain {

	public static void main(String[] args) {

		SmartPhone sp = new SmartPhone("삼성", "안드로이드");
		
		String result1 = sp.toString();
		System.out.println(result1);
		
		SmartPhone sp2 = new SmartPhone("애플", "IOS");
		String result2 = sp2.toString();
		System.out.println(result2);
		
	}
}

 

 

 

 

# hashCode() 메서드

 

- 객체의 해시코드(정수 값)(hash code)를 반환하는 메서드

- Object 클래스의 hashCode()는 객체의 주소를 int로 변환해서 반환

- equals()를 오버라이딩 하면, hashCode()도 오버라이딩 해야 한다.

- equals()를 오버라이딩 하는 이유는 기존 주소로 비교를 하던 것을 iv(인스턴스 변수)를 보고 비교하는것으로 변환시키기 위해서다.

- equals()의 결과가 true인 두 객체의 해시코드는 같아야 하기 때문이다.



-객체의 해시코드란?
 # 객체를 식별할 하나의 정수값을 말한다.
 # Object의 hashCode() 메소드는 객체의 메모리 번지를 이용하여 해시코드를 만들어 리턴한다.

-컬렉션 프레임워크에서 HashSet, HashMap, Hashtable은 다음과 같은 방법으로 두 객체가 동등한지 비교한다. 
-우선 hashCode() 메소드를 실행해서 리턴된 해시코드 값이 같은지를 본다. 
-해시코드 값이 다르면 다른 객체로 판단하고, 해시코드 값이 같으면 equals() 메소드로 다시 비교한다. 
  그렇기 때문에 hashCode() 메소드가 true가 나와도 equals()의 리턴값이 다르면 다른 객체가 된다.

 

 

-클래스를 통해 객체를 생성해서 값을 넣으려면 hashCode()의 리턴값이 필요하고, equals()의 리턴값이 필요하다.


더보기
package chapter10.example03;

public class Key{

	int number;
	
	public Key(int number){
		this.number = number;
		
	}
	
	@Override
	public boolean equals(Object obj){
		System.out.println("equals() 호출");
		if(obj instanceof Key) {
			Key compareKey = (Key) obj;
			// 필드 값이 기본형 int이기에 equals를 쓰지 않는다.
			if(this.number == compareKey.number) {
				return true;
			}
		}
		return false;
	}
	
	@Override
	public int hashCode(){
		System.out.println("hashCode() 호출");
		return this.number;
	}
	
}
package chapter10.example03;
import java.util.HashMap;

public class KeyMain {

	public static void main(String[] args) {
		
		
		HashMap<Key, String> hashMap = new HashMap<Key, String>();
		
		
		hashMap.put(new Key(1), "번호선별");
		
		String value = hashMap.get(new Key(1));
		
		System.out.println(value);
	}
}

 

-클래스를 통해 객체를 생성해서 값을 넣으려면 hashCode()의 리턴값이 필요하고, equals()의 리턴값이 필요하다.

 

 

-Integer로 값을 넣으면, String에 해당하는 항목을 가져온다.

-찜질방에서 물건을 보관함에 맡기면 번호키를 받는데 그런 개념과 비슷하다고 보면 된다.

(45번 보관함에 String이라는 물건을 넣음)
여기서, Integer로 값을 넣게 되면 위와 같이 Key 객체를 만들어줄 필요는 없는데, 객체를 해쉬태그 값으로 넣을 수 있다는 것을 보여주기 위해서 넣은 것임.

더보기
package chapter10.example03;
import java.util.HashMap;

public class KeyMain {

	public static void main(String[] args) {
		
		
		HashMap<Integer, String> hashMap = new HashMap<Integer, String>();
		
		
		hashMap.put(1, "번호선별");
		
		String value = hashMap.get(1);
		
		System.out.println(value);
	}
}

-이렇게 사용도 가능하다.

 


 

 

 

##  객체 복제( clone() )

 


객체 복제는 원본 객체의 필드값과 동일한 값을 가지는 새로운 객체를 생성하는 것을 말한다. 
객체를 복제하는 이유는 원본 객체를 안전하게 보호하기 위해서이다.  
신뢰하지 않는 영역으로 원본 객체를 넘겨 작업할 경우 원본 객체의 데이터가 훼손될 수 있기 때문에 
복제된 객체를 만들어 신뢰하지 않는 영역으로 넘기는 것이 좋다.  
복제된 객체의 데이터가 훼손되더라도 원본 객체는 아무런 영향을 받지 않기 때문에 
안전하게 데이터를 보호할 수 있게 된다. 
객체를 복제하는 방법에는 얕은 복제와 깊은 복제가 있다.

 

-얕은 복제 : 참조하는 데이터 주소가 같은 객체가 생김

-깊은 복제 : 참조하는 데이터 주소가 다른 객체가 생김



# 얕은 복제(thin clone)

 

 

-참조하는 데이터 주소의 값을 복사해서 참조하는 데이터가 같은 객체를 복사

얕은 복제 (thin clone) 란 단순히 필드값을 복사해서 객체를 복제하는 것을 말한다. 
필드값만 복제하기 때문에 필드가 기본 타입일 경우 값 복사가 일어나고 
필드가 참조 타입일 경우에는 객체의 번지가 복사된다. 

예를 들어 원본 객체에 int 타입의 필드와 배열 타입의 필드가 있을 경우,
얕은 복제제가된 객체의 필드값은 다음과 같다.
                  
Object의 clone() 메소드는 자신과 동일한 필드값을 가진 얕은 복제된 객제를 리턴한다. 
이 메소드로 객제를 복제하려면 원본 객체는 반드시 java.lang.Cloneable 인터페이스를 구현하고 있어야 한다. 
메소드 선언이 없음에도 불구하고 Cloneable 인터페이스를 명시적으로 구현하는 이유는 
클래스 설계자가 복제를 허용한다는 의도적인 표시를 하기 위해서이다. 
클래스 설계자가 복제를 허용하지 않는다면 Cloneable 인터페이스를 구현하지 않으면 된다. 

Cloneable 인터페이스를 구현하지 않으면 clone() 메소드를 호출할 때 
CloneNotSupportedException 예외가 발생하여 복제가 실패된다. 
clone()은 CloneNotSupportedException 예외 처리가 필요한 메소드이기 때문에 try - catch 구문이 필요하다.

try {
Object obj = clone ();
} catch (CloneNotSupportedException  e ) { 

 

 


더보기

 

package chapter10.example04;

// 얕은 복제 (컴파일 에러가 발생되지 않는데 Cloneable은 추상 메서드가 없기 때문이다)
public class Member implements Cloneable {	// Cloneable 을 구현한 클래스만 clone() 메서드로 복제가능
	
	// 필드
	public String id;
	public String password;
	public int age;
	public boolean adult;
	int [] scores;
	
	// 생성자
	public Member(String id, String password, int age, boolean adult, int [] scores) {
		this.id =id;
		this.password = password;
		this.age = age;
		this.adult = adult;
		this.scores = scores;
	}
	
	// 메서드 (객체를 리턴하는 메서드) 복사하는 코드
	public Member getMember() {
		// Member 타입의 cloned라는 변수에 null 값을 넣어줌
		Member cloned = null;
		try {
			cloned = (Member) clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return cloned;
		
	}
}
package chapter10.example04;

public class MemberMain {

	public static void main(String[] args) {
		
		Member original = new Member("abcd", "1234", 28, true, new  int []  {90 , 100});
		
		Member cloned = original.getMember();
		
		cloned.scores[0] = 50;
		
		System.out.println("##### 원복 객체의 필드값 #####");
		System.out.println("id : " + original.id);
		System.out.println("pw : " + original.password);
		System.out.println("age : " + original.age);
		System.out.println("adult : " + original.adult);
		System.out.println("scores : " + original.scores[0]);
		System.out.println("scores : " + original.scores[1]);
		
		System.out.println("##### 복제된 객체의 필드값 #####");
		System.out.println("id : " + cloned.id);
		System.out.println("pw : " + cloned.password);
		System.out.println("scores : " + cloned.scores[0]);
		System.out.println("scores : " + cloned.scores[1]);
	}
}

 


 

 

더보기
package chapter10.example05;

import java.util.Arrays;

public class Member implements Cloneable {

	public int age;
	public String name;
	public int [] scores;		// 참조 타입 배열 필드 생성
	public Car car;					// 참조 타입 Car 필드 생성
	
	public Member(int age, String name, int [] scores, Car car) {
		this.age = age;
		this.name = name;
		this.scores = scores;
		this.car = car;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// 얕은 복제
		Member cloned = (Member) super.clone();
		
		// 깊은 복제
		cloned.scores = Arrays.copyOf(this.scores, this.scores.length);
		cloned.car = new Car(this.car.model);
		return cloned;
	}
	
	public Member getMember() {
		Member cloned = null;
		try {
			cloned = (Member) clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return cloned;
	}
}
package chapter10.example05;

public class Car {

	String model;
	
	public Car(String model) {
		this.model = model;
	}
}

 

 

-깊은 복제는 이처럼 결과값을 각각의 객체마다 바꿀 수 있다.

package chapter10.example05;

public class MemberMain {

	public static void main(String[] args) {

		Member original = new Member(20, "빛", new int [] {50, 60, 70 , 80}, new Car("그랜져"));
		
		Member cloned = original.getMember();
		
		original.scores[0] = 100;
		original.scores[2] = 1500;
		original.car.model = "포르쉐 911";
		
		System.out.println("#### 원본 객체 정보 ####");
		System.out.println("name : " + original.name);
		System.out.println("age : " + original.age);
		for (int i = 0; i < original.scores.length; i++) {
			System.out.print(original.scores[i] + " ");
		}
		System.out.println();
		System.out.println("model : " + original.car.model);
		System.out.println("\n");
		
		cloned.scores[2] = 3800;
		
		System.out.println("#### 복사된 객체 정보 ####");
		System.out.println("name : " + cloned.name);
		System.out.println("age : " + cloned.age);
		for (int i = 0; i < cloned.scores.length; i++) {
			System.out.print(cloned.scores[i] + " ");
		}
		System.out.println();
		System.out.println("model : " + cloned.car.model);
		System.out.println("\n");
		
	}
}

 

 

 

 

 

# 객체 소멸자(finalize())

 

객체를 강제로 소멸시킨다. 잘 사용하지는 않는다.

더보기
package chapter10.example05;

public class Counter {

	// 필드
	public int no;
	// 생성자
	public Counter(int no) {
		this.no = no;
	}
	
	// 메서드 (객체 소멸자)
	@Override
	protected void finalize() throws Throwable {
		System.out.println(no + "번 객체가 finalize() 메서드를 호출");
//		super.finalize();	// 사용을 권장하지 않을 때 이런 표시가 뜬다.
	}
}
package chapter10.example05;

public class FinalizationMain {

	public static void main(String[] args) {

		Counter counter = null;
		
		for (int i = 0; i < 500; i++) {
			counter = new Counter(i);
			counter = null;
			System.gc();
		}
	}
}

 

 

 

 

 

# 쓰레기 수집기 실행 요청(gc())

 

 

## System 클래스

자바 프로그램은 운영체제상에서 바로 실행되는 것이 아니라 JVM 위에서 실행된다. 
따라서 운영체제의 모든 기능을 자바코드로 직접 접근하기란 어렵다 
하지만 java.lang 패키지에 속하는 System 클래스를 이용하면 운영체제의 일부 기능을 이용할 수 있다 
즉, 프로그램 종료, 키보드로부터 입력, 모니터로 출력 메모리 정리 현재 시간 읽기 
시스템 프로퍼티 읽기 환경 변수 읽기 등이 가능하다. 
System 클래스의 모든 필드와 메소드는 정적 (static) 필드와 정적 (static) 메소드로 구성되어 있다


# 쓰레기 수집기 실행( gc() )
자바는 개발자가 메모리를 직접 코드로 관리하지 않고 JVM이 알아서 자동으로 관리한다.
JVM은 메모리가 부족할 때와 CPU가 한가할 때에 
쓰레기 수집기(Garbage Collector) 를 실행시켜 사용 하지 않는 객체를 자동 제거한다. 


쓰레기 수집기는 개발자가 직접 코드로 실행시킬 수 없고 JVM에게 가능한한 빨리 실행해 달라고 요청할 수는 있다. 
이것이 System.gc() 메소드이다. 
System.gc() 메소드가 호출되면 쓰레기 수 집기가 바로 실행되는 것은 아니고, 
JVM은 빠른 시간 내에 실행시키기 위해 노력한다.

(뭔가 좀 귀엽다)

 

더보기
package chapter10.example05;

public class Employee {

	public int empno;
	
	public Employee(int empno) {
		this.empno = empno;
		System.out.println(empno + "번 객체가 생성");
	}
	
	@Override
	protected void finalize() throws Throwable {
		System.out.println("Employee" + this.empno + "가 메모리에서 제거됩니다.");
	}
}
package chapter10.example05;

public class GcMain {

	public static void main(String[] args) {

		Employee emp;
		
		emp = new Employee(1);
		emp = null;
		emp = new Employee(2);
		emp = new Employee(3);
		emp = new Employee(4);
		
		System.out.println("현재 사용되는 객체는 " + emp.empno);
		System.gc();		// GC 호출
	}
}

 

 

 

 

 

 

# 현재 시각 읽기( currentTimeMillisO. nanoTime() )



System 클래스의 currentTimeMillis() 메소드와 nanoTime() 메소드는 컴퓨터의 시계로부터 
현재 시간을 읽어서 밀리세컨드(1 /1000초) 단위와 나노세컨드(1 /10^9초) 단위의 long 값을 리턴한다.
(nanoTime은 10억분에 1초를 의미한다.)

long time = System.currentTimeMillis() ; 
long time = System.nanoTime();


리턴값은 주로 프로그램의 실행 소요 시간 측정에 사용된다. 
프로그램 시작 시 시각을 읽고, 프로그램이 끝날 때 시각을 읽어서 차이를 구하면 프로그램 실행 소요 시간이 나온다. 
다음 예제는 for문을 사용해서 1 부터 1000000까지의 합을 구하는데 걸린 시간을 출력한다.

 

더보기
package chapter10.example06;

public class SystemTimeExample {

	public static void main(String[] args) {
		
		
		// nanoTime은 10억분의 1을 반환하기 때문에 long 타입으로 지정해줘야 한다.
		long startTime = System.nanoTime();
		
		long sum = 0;
		for (int i = 1; i <= 1000000; i++) {
			sum += i;
		} 
		long endTime = System.nanoTime();
		System.out.println("1부터 1,000,000까지의 총합 = " + sum);
		System.out.println("계산에 이 컴퓨터가 걸린 시간은 " +  (endTime - startTime) + "나노초가 소요되었습니다.");
	
		System.out.println();
		
		long startTime2 = System.currentTimeMillis();
		
		long sum2 = 0;
		for (int i = 1; i <= 1000000; i++) {
			sum2 += i;
		} 
		long endTime2 = System.nanoTime();
		System.out.println("1부터 1,000,000까지의 총합 = " + sum2);
		System.out.println("계산에 이 컴퓨터가 걸린 시간은 " +  (endTime2 - startTime2) + "나노초가 소요되었습니다.");
		
	}
}

 

 

 

 

반응형

'java(2)↗' 카테고리의 다른 글

java Arrays 클래스, Boxing 박싱, Date, Format  (0) 2024.05.01
java String 클래스  (0) 2024.04.29
java 예외  (0) 2024.04.25
java 인터페이스  (0) 2024.04.24
java 다형성  (0) 2024.04.23