개발자 끄적끄적
Java Lang 패키지, Hashcode, 깊은 복사, 얕은 복사 본문
[import gui.*의 의미]
- gui안에 있는 모든 클래스
gui 클래스
1. A클래스
2. B클래스
3. C클래스
4. ABC클래스
1)D
2)E
3)F 가 있을 때
gui.*는 A, B, C, ABC클래스만 참조되고 D, E, F는 포함하지 않는다
D, E, F를 포함하려면 import gui.ABC.*라고 써야한다
<자바 가상 머신(JVM, Java Virtual Machine)>
- 자바 프로그램이 어느 기기, 어느 운영체제 상에서도
실행할 수 있게 만들어 준다 => WORA
- 자바 프로그래의 메모리를 효율적으로 관리&최적화해준다
- 프로그램을 실행하는 자바 플랫폼 구성요소이다
*가비지 컬렉션(Garbage Collection)
- JVM이 메모리를 관리하는 프로세스를 지칭하는 용어이다
- 자바 프로그램 상에서 사용하지 않은 메모리를 지속적으로 찾아 제거함으로써
효율적인 메모리 관리를 가능케 한다
<자바 런타임 환경(JRE, Java Runtime Environment)>
- JVM이 원활하게 잘 작동할 수 있도록 환경을 제공해주는 역할을 한다
- 자바 클래스 라이브러리(Java Class Libraries), 자바 가상 머신(JVM),
자바 클래스 로더(Jave Class Loader)를 포함한다
- JVM을 생성하는 디스크 상의 부분이다
- 클래스 로더, 클래스 라이브러리를 통해 작성한 자바 코드를 라이브러리와 결합 후
JVM에 넘겨 실행시킨다
*자바 8의 메모리 관리
- 자바 메모리는 힙, 스택, 메타 스페이스로 구성되어 있다
- 힙 : 자바가 변수 내용을 저장하는 장소
- 스택 : 자바가 함수 실행 및 변수 참조를 저장하는 장소
- 메타 스페이스(과거 펌젠) : 자바가 클래스 정의와 같이 프로그램에서 변화하지 않는
정보를 저장하는 장소
<자바 개발 키드(JDK, Java Development Kit)>
- 개발자들이 JVM과 JRE에 의해 실행되고 구동될 수 있는 자바 프로그램을
생성할 수 있게 해준다
- JDK를 설치하면 JRE가 자동으로 설치된다
- JDK는 JRE를 포함하고 JRE는 JVM을 포함한다
- JDK에는 JRE가 없는 "자바 컴파일러(javac, java compiler)"를 포함한다
- .java파일을 만들어 실행(빌드)하면 컴파일 작업을 거쳐 .class라는 파일이 자동으로
생성된다
<접근자 - 포함관계>
1. public - 아무나 접근
2. protected - 같은 패키지, 상속한 클래스(친척)
3. package(default) - 클래스가 포함되어 있는 폴더(package라고 하는 접근자는 X)
4. private
@Override
protected Object clone() throws CloneNotSupportedException{ ... }
- Override에서 접근제한자 protected는 protected나 public으로 바꿀 수 있다(크거나 같이야 한다)
- 예외처리(throws)는 부모가 가지고 있는 예외보다 자식은 작거나 같아야 한다
-------------------------복습-------------------------
<Java.lang 패키지>
- 자바 프로그램에서 기본적으로 사용되어야 하는 클래스들을 포함하고 있으며,
import문장을 사용하지 않고도 사용할 수 있도록 되어있다
- 자동 import이다
<Object>
- 자바의 모든 클래스의 최고 조상이 되는 클래스이다
- 멤버 변수는 없고 11개의 메서드만을 제공한다
- 자동상속이다
protected Object clone() : 객체 자신의 복사본을 반환한다
public boolean equals(Object obj) : 객체 자신과 객체 obj과 같은 객체인지 알려준다
pulic int hashCode() : 자신의 해시코드를 반환한다
public String toString() : 자신의 정보를 문자열로 반환한다
public void notify() : 자신이 사용하려고 기다리는 스레드를 깨운다
public void notifyAll()
public void wait() : 다른 스레드가 notify()나 notifyAll()을 호출할 때까지 현재 스레드를 기다리게 한다
public void wait(long timeout) : timeout(천분의 1초)
public void wait(long timeout, int nanos) : nanos(10^9분의 1초)
public Class getClass() : 자신의 객체 정보를 담고 있는 Class 인스턴스를 반환한다
protected void finalize() : 객체가 소멸될 때 가비지 콜렉션에 의해 자동으로 호출된다
*garbage collection(사용하지 않는 메모리) : system.gc()
<Clone 메서드 - Object라고 하는 최상위 조상이 갖고 있는 메서드>
- 기본형의 필드값은 복사되지만 참조형 필드 값은 완전히 복사가 되지 않고
얕은 복사가 된다
*깊은 복사(Deep Copy) : 독립적인 객체 생성, '실제 값'을 새로운 메모리 공간에 복사하는 의미
*얖은 복사(Shallow Copy) : '주소 값'을 복사하는 의미, 주소 값을 복사하기 때문에
참조하고 있는 실제 값은 같다(복사가 됐다고 해서 독립적인 객체는 아니다)
- 배열과 같은 참조형 멤버를 깊은 복사하려면 clone메서드를 재정의하여 처리해야 한다
- Cloneable 인터페이스를 구현한 클래스에서만 clone메서드를 사용할 수 있다
*~able이 붙어 있으면 100% interface이다 ex)Runnable
*기본형 필드(8가지) : byte~double
*참조형 필드(new) : 기본형 필드를 제외한 나머지
*int a = 10;
A a = new A(); //A는 참조형 필드
A b = a;
a라고 하는것이 b에 대입 된다
int k = 10;
int m =k; 일 때
k에 10이 들어갔으니 k=10, m=10
->"A라고 하는 참조형 필드는 얕은 복사가 된다"
k라는 공간(메모리)의 초기값은 10 일 때
m이라는 공간(메모리)는 별도로 존재하지만 k라는 공간을 가리키게 된다
즉, 값을 "공유"하며 독립적인 값을 갖지는 못한다
ex)
public class Point implements Cloneable{
int x;
int y;
public Object clone(){
Object o = null;
try{
o = super.clone(); //'super'=부모 즉, super.clone();은 Cloneable을 뜻한다(=재귀호출)
}catch(Exception ex){}
return o;
}
}
public class CloneTest{
public CloneTest(){
Point p = new Point();
p.x = 100; //p의 필드(int)가 가지고 있는 x
p.y = 200;
Point temp = (Point)p.clone(); //p의 포인트를 호출해서 '캐스팅(Point)'
//p.clone();은 Object(최상위)이며 Object를 반환하는 것을 Point로 캐스팅했다
//Object는 무조건 상속받는다
temp.x=50;
temp.y=50;
System.out.println("p.x=" + p.x);
System.out.println("temp.x=" + temp.x);
System.out.println(p==temp); //false
System.out.println(p.equals(temp)); //false 즉, p라고하는 클래스와 temp라고 하는 클래스는 독립적이다
}
public static void main(String[]args){
new CloneTest();
}
}
- 상속
1. extends(확장)
2. implements(구현)<- 추상메서드를 재정의 해라
- 두 객체의 비교값은 모두 false가 나온다 즉, 서로 다른 객체임을 의미한다
ex)
package lang;
public class CloneTest {
CloneTest(){ //생성자의 접근제한자 default 즉, 같은 패키지 내에서만 접근 가능
Point p = new Point();
p.x=100;
p.y=200;
Point temp = (Point)p.clone(); //깊은 복사
temp.x=50;
temp.y=50;
System.out.println("p.x=" + p.x); //100
System.out.println("temp.x=" + temp.x); //50
System.out.println(p==temp); //false
System.out.println(p.equals(temp)); //false
//-------------------------------------
Point p2 = p; //독립적인 객체가 아닌 같은 객체를 복사했다(값을 공유) p는 얕은 복사
p2.x=10;
p2.y=20;
System.out.println("----------------");
System.out.println("p.x" + p.x); //10
System.out.println("p2.x" + p2.x); //10
System.out.println(p==p2); //true
System.out.println(p.equals(p2)); //true
}
public static void main(String[] args) {
new CloneTest(); //익명 객체를 만들어 실행
//==CloneTest ct = new CloneTest();
}
}
<hashCode 메서드>
- hashCode는 객체를 식별하는 정수값이다
- 객체가 생성된 메모리 번지를 기본으로 정수값이 만들어지기 때문에 객체 마다
서로 다른 값을 갖는다
ex)
public class HashCodeObject{
}
public class HashCodeObjectTest{
public static void main(String[]args){
HashCodeObject o1 = new HashCodeObject();
HashCodeObject o2 = new HashCodeObject();
System.out.println(o1.hashCode()); //1521118594
System.out.println(o2.hashCode()); //1940030785
System.out.println(o1==o2); //
}
}
결과 : 실행할 때 마다 다른 값이 나올 수 있다
1521118594
1940030785
false
- 같은 클래스를 사용하여 객체를 생성하더라도 hashcode값이 다르기 때문에
같은 객체로 인식하지 않는 것이다
ex)논리적으로 클래스가 갖고 있는 특정한 값이 같다면 같은 객체로 인식하는 코드
public class HashCodeObject{
int id;
public HashCodeObject(int id){
this id. = id;
}
@Override
public int hashCode(){
return id;
}
public String toString(){
return super.toString() + "," + super.hashCode();
}
}
public class HashCodeObjectTest{
public static void main(String[]args){
HashCodeObject o1 = new HashCodeObject(10);
HashCodeObject o2 = new HashCodeObject(10);
HashCodeObject o3 = o1;
System.out.println(o1.hashCode()); //10
System.out.println(o2.hashCode()); //10
System.out.println(o3.hashCode()); //10
System.out.println(o1.toString()); //d_class.HashCodeObject@a,1521118594
System.out.println(o2.toString()); //d_class.HashCodeObject@a,1940030785
System.out.println(o3.toString()); //d_class.HashCodeObject@a,1521118594
System.out.println(o1==o2); //false
System.out.println(o1.equals(o2)); //false
System.out.println(o1==o3); //true
System.out.println(o1.equals(o3)); //true
}
}
결과 :
10
10
10
d_class.HashCodeObject@a,1521118594
d_class.HashCodeObject@a,1940030785
d_class.HashCodeObject@a,1521118594
false
false
true
true
- hashCode값이 동일하면 동일한 객체로 인식하는 것이 아닌 서로 다른 객체로 인식한다
- HashCodeObject의 hashCode값은 같았을 지 모르지만 부모 객체인 Object의
hashCode값은 다르게 나온다
이는 즉, Object 객체가 갖고 있는 equals 메서드를 재정의하지 않았디 때문이다
ex)
package lang;
public class HashCodeObject2 {
int id;
HashCodeObject2(int id){
this.id = id; //생성자를 통해서 id값을 전달받는다
}
//hashcode메서드 오비라이딩(재정의)
@Override
public int hashCode() {
return this.id;
}
@Override
public String toString() {
return super.toString() + "," + super.hashCode();
}
}
package lang;
public class HashCodeObject3 {
int id;
HashCodeObject3(int id){
this.id = id;
}
@Override
public int hashCode() {
return this.id;
}
@Override
//t.equals(t2));에서 equals가 실행되고 t2=obj
public boolean equals(Object obj) {
boolean b = false;
if(obj instanceof HashCodeObject3) {
//매개변수의 obj가 HashCodeObject3이라는 클래스로 만들어 진 것이니?
☆HashCodeObject3 target = (HashCodeObject3)obj;☆
//obj를 HashCodeObject3으로 강제로 형변환하여
//target이라는 곳에 대입
if(target.id == this.id) { //t1의 id값과 t2의 id값이 같니?
b=true;
}
}
return b;
}
}
package lang;
public class HashCodeObjectTest {
public static void main(String[] args) {
HashCodeObject o1 = new HashCodeObject();
HashCodeObject o2 = new HashCodeObject();
System.out.println("o1.hashcode :" + o1.hashCode()); //1023487453
System.out.println("o2.hashcode :" + o2.hashCode()); //1651191114
System.out.println("o1==o2 :" + (o1==o2)); //false
System.out.println("----------------------");
HashCodeObject2 o3 = new HashCodeObject2(100);
HashCodeObject2 o4 = new HashCodeObject2(100);
System.out.println("o3.hashcode :" + o3.hashCode()); //100
System.out.println("o4.hashcode :" + o4.hashCode()); //100
//이유 id값을 강제로 설정했기 때문에 hashcode값이 같다
System.out.println("o3==o4 :" + (o3==o4)); //false
System.out.println("o3.equals(o4) :" + o3.equals(o4)); //false
System.out.println("----------------------");
System.out.println(o1.toString());
//출력 : lang.HashCodeObject@3d012ddd
System.out.println(o2.toString());
//출력 : lang.HashCodeObject@626b2d4a
System.out.println(o3.toString());
//출력 : lang.HashCodeObject2@64,474675244
System.out.println("----------------------");
HashCodeObject3 t1 = new HashCodeObject3(1); //id값=1
HashCodeObject3 t2 = new HashCodeObject3(1); //id값=1
HashCodeObject3 t3 = new HashCodeObject3(2);
System.out.println(t1==t2); //false
System.out.println(t1.equals(t2)); //☆true☆
System.out.println(t1==t3); //false
System.out.println(t1.equals(t3)); //false
}
}
<hashCode 로직>
[hashCode()리턴값]
1. 다름 -> 다른 객체
2. 같음 ->[equals()리턴값]
1. true : 동등 객체
2. false : 다른 객체
<equals 메서드>
- 객체가 생성되어 저장된 객체의 주소값으로 같은 지를 판별하게 된다
- '=='로 비교하든, equals메서드를 사용하여 비교하든 같은 값이 나온다
ex)
package d_class;
public class EqualsTest{
public static void main(String[]args){
Object o1 = new Object();
Object o2 = new Object();
System.out.println(o1==o2); //false
System.out.println(o1.equals(o2)); //false
Object o3=o1;
System.out.println(o1==o3); //true
System.out.println(o1.equals(o2)); //true
}
}
- 객체가 같고 있는 값들과 같은 지를 판별하여 같은 객체인가를 판별하려면
equals메서드를 재정의해서 코딩한다
ex)id가 동일하면 동일객체로 판별
public class EqualsObject{
String id;
@Override
public boolean equals(Object obj){
boolean b = false;
if(obj intanceof EqualsObject){
EqualsObject eo = (EqualsObject)obj;
b=true;
}
}
return b;
}
}
public class EqualsObjectTest{
public static void main(String[]agrs){
EqualsObject o1 = new EqualsObject();
EqualsObject o2 = new EqualsObject();
EqualsObject o3 = new EqualsObject();
o1.id = "hong";
o2.id = "hong";
o2.id = "kim";
System.out.println(o1==o2); //false
System.out.println(o1==o3); //false
System.out.println(o1.equals(o2)); //true
System.out.println(o2.equals(o3)); //false
}
}
- equals메서드를 재정의한다고 하더라도 '=='로 비교할 경우 서로 다른 객체라고 판단
- 두 객체가 완전 동등 객체임을 비교해야 하는 프로그램이라면,
hashCode메서드와 equals메서드를 함께 재정의 해야한다
'JAVA' 카테고리의 다른 글
Java Lang 패키지 - String, 컬렉션(List) (0) | 2023.03.04 |
---|---|
GUI 프로그램 (0) | 2023.03.04 |
클래스, 생성자 (0) | 2023.03.04 |