프로그램의 실행에 필요한 메모리 공간을 가리켜 -> 메인 메모리라고 한다. 물적으로는 RAM을 의미한다.
자바 가상머신 메모리
메소드, stack, Heap 영역 이렇게 나뉘어 진다.]
메소드 영역
자바 가상머신에 의해 실행이 가능한 코드를 가리켜 바이트코드라고 한다.
public class Main_1 {
public static void main(String[] args) {
Boy boy = new Boy(); //인스턴스 변수 생성
Boy.average += 5; // 클래스 변수 생성
}
}
인스턴스 생성 및 클래스 변수의 접근을 위해서는, 해당 클래스의 바이트코드가 메모리 공간에 로딩. -> 이때 로딩 되는 영역을 메소드 영역
Stack 영역
스택은 지역변수와 매개변수가 저장되는 공간
중괄호로 구분되는 지역 내에서만 유효한 변수들
중괄호를 벗어나면 바로 소멸되는 특성의 데이터를 저장 하기 위한 영역
public static void main(String[] args) {
int num1 = 10;
int num2 = 10;
adder(num1, num2);
System.out.println("end Program");
}
public static int adder(int a, int b){
int rst = a + b;
return rst;
}
}
main 메소드가 호출되고 num1과 num2가 스택에 할당된 상황. Adder 메소드가 호출된 이후 상황은 a, b에 스택이 할당이 되고, rst 변수도 할당. 또한 main 메서드도 종료된 상황이 아니기에 같이 쌓여있는 상황
adder 메서드 빠져나오면, 할당된 지역변수와 매개변수가 스택에서 모두 소멸. 지역변수와 매개변수 선언되는 순간 스택에 할당되었다가. 할당 영역을 벗어나면, 소멸
Heap 영역
인스턴스는 Heap 영역에 할당. 인스턴스를 스택 구간이 아닌 힙 영역에 할당하는 이유는 인스턴스의 소멸 시점과 소멸 방법이 지역변수와는 다르기 때문.
public static void main(String[] args) {
String str1 = new String("my String");
String str2 = new String("my String");
}
}
str1 -> my String heap 영역 참조, str2 -> myString heap 영역 참조. 인스턴스의 소멸시기를 결정하는 것은 가상머신
자바의 가상머신의 인스턴스 소멸시기
public static void main(String[] args) {
String str1 = new String("my String");
String str2 = new String("my String");
str1 = null;
str2 = null;
}
}
이와 같은 인스턴스 상태는 존재할 수 없다. 더 이상 접근 할 수 없는 인스턴스. 인스턴스는 소멸 대상이 되어 가상머신에서 소멸이 이뤄진다. 자바의 인스턴스 소멸방식을 가리켜 가비지 컬랙션이라고 하며, 힙 영역은 가상머신에 의한 가비지 컬렉션이 일어나는 메모리 공간.
Object 클래스
인스턴스 소멸시 해야 할 일 -> finalize 메소드 사용
class Person{
String name;
Person(String name){
this.name = name;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println(name);
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("Kim");
Person p2 = new Person("Lee");
p1 = null; p2 = null;
System.out.println("end of Program");
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println(name);
}
}
상위 클래스에 finalize 사용
상위 클래스의 finalize 메소드에 삽입되어 있는 코드들 실행. 다른 클래스들을 상속한 경우, 오버라이딩 하는 상위 클래스의 메소드가 본인이 정의한 클래스가 아니라면, Object와 같이 자바에서 제공하는 메소드라면, 오버라이딩 된 메소드를 호출 하는 것 안전.
두 인스턴스를 가비지 컬랙션의 대상이 되게 하였음에도 불구하고, finalize 메소드가 호출된 흔적은 볼 수 없다.
가비지 컬랙션은 빈번히 일어나지 않는다.
소멸한 인스턴스가 생겨도 가비지 컬랙션으로 이뤄지지 않는다.
실행 중인 자바 프로그램이 종료되면 -> 할당된 메모리 전부가 해제되듯, finalize 메소드의 호출이 생략 가능성. System 클래스에 정의 된 두 메소드를 순차적 호출을 통해서, finalize 메소드 호출을 어느정도 보장 받을 수 있다.
Public static void gc() = 가비지 컬랙션의 수행을 요청 (명령 X, 요청)
Public static void runfFinalization() = 소멸이 보류된 인스턴스의 finalize 메소드 호출 요청
무조건 가비지 컬랙션이 실행이 되는 것이 아니라, 가상머신이 최선을 다해 노력. & 가비지 컬랙션이 수행이 된다 한들, 소멸 대상을 그 순간 한번에 소멸 시키지는 않는다.
인스턴스의 equals 메소드 비교
public class Main2 {
public static void main(String[] args) {
String str1 = new String("simple");
String str2 = new String("simple");
if (str1 == str2){
System.out.println("same");
} else {
System.out.println("diff");
}
if (str1.equals(str2)){
System.out.println("same");
} else {
System.out.println("diff");
}
}
}
결과에 따라 true 또는 false를 리턴
== 연산의 경우 -> 참조 값 비교는 가능,
Equals의 경우 비교가 이뤄지도록 오버리이딩 하라고 존재하는 메소드이다.
따라서 두 인스턴스의 내용 비교를 원한다면, equals 메소드 활용. 단순 참조변수의 참조 값만의 비교를 원한다면 ==을 사용.
<문제>
class Point {
private int xPos;
private int yPos;
public Point(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
public boolean equals(Object o) {
Point p = (Point) o;
if (xPos == p.xPos && yPos == p.yPos) {
return true;
} else {
return false;
}
}
}
class Rectangle {
private Point upperleft;
private Point lowerRignt;
public Rectangle(int x1, int y1, int x2, int y2) {
this.upperleft = new Point(x1, y1);
this.lowerRignt = new Point(x2, y2);
}
public boolean equals(Object o){
Rectangle r = (Rectangle) o;
if(upperleft.equals(r.upperleft) && lowerRignt.equals(r.lowerRignt))
return true;
else
return false;
}
}
public class Main3 {
public static void main(String[] args) {
Point point = new Point(3,4);
Point point1 = new Point(3,4);
Rectangle rectangle = new Rectangle(1,2,2,2);
Rectangle rectangle1 = new Rectangle(1,2,2,2);
if (point1.equals(point1))
System.out.println("same");
else
System.out.println("false");
if (rectangle1.equals(rectangle1)){
System.out.println("s");
} else {
System.out.println("Diff");
}
}
}
인스턴스 복사 clone 메소드
호출한 메소드가 속한 인스턴스의 복사본이 생성되고, 참조값 반환. 단, 인터페이스를 구현한 인스턴스를 대상으로만, 위의 메소드를 호출 할 수 있다.
Interface Cloneable
인터페이스를 구현한 클래스의 인스턴스만 clone 메소드 호출
Cloneable 인터페이스를 구현하지 않은 클래스의 인스턴스를 대상으로 clone 메소드를 호출하면 예외 발생.
이 클래스의 인스턴스는 복사 ok.
사실 Cloneable 인터페이스는 마커 인터페이스이다. 정의 해야 할 메소드가 존재하지 않는 복사를 해도 된다는 표식. 인스턴스 복사 허용 여부는 클래스를 정의하는 과정에서 고민
class Point1 implements Cloneable{
private int xPos;
private int yPos;
public Point1(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
public void showInfo(){
System.out.printf("[%d %d]", xPos, yPos);
System.out.println();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main4 {
public static void main(String[] args) {
Point1 p = new Point1(3,5);
Point1 cpy;
try {
cpy = (Point1) p.clone();
p.showInfo();
cpy.showInfo();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
인스턴스의 복사가 정상적으로 이뤄짐
Stack 영역 -> Heap 영역
참조변수 -> 인스턴스 (clone 복사)
참조변수 -> 인스턴스
상위 클래스, 즉 Object 클래스의 clone 메소드를 호출.
Object 클래스의 clone 메소드는
Protected Object clone() throws cloneNotSupportedException
Protected로 선언되어 있던 clone 메소드를 오버라이딩 하여 public으로 변경 범위를 넓히는 것은 가능하지만, 범위를 좁히는 것은 불가능 하다.
class Point2 implements Cloneable {
private int xPos;
private int yPos;
public Point2(int xPos, int yPos) {
this.xPos = xPos;
this.yPos = yPos;
}
public void showInfo() {
System.out.printf("[%d %d]", xPos, yPos);
System.out.println();
}
public void changePost(int x, int y) {
xPos = x;
yPos = y;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Rectangle2 implements Cloneable {
private Point2 upperLeft;
private Point2 lowerRight;
public Rectangle2(int x1, int y1, int x2, int y2) {
this.upperLeft = new Point2(x1, y1);
this.lowerRight = new Point2(x2, y2);
}
public void changePos(int x1, int y1, int x2, int y2) {
upperLeft.changePost(x1, y1);
lowerRight.changePost(x2, y2);
}
@Override
public Object clone() throws CloneNotSupportedException {
Rectangle2 copy = (Rectangle2) super.clone();
copy.upperLeft = (Point2) upperLeft.clone();
copy.lowerRight = (Point2) lowerRight.clone();
return copy;
}
public void showPosition() {
System.out.println("좌측상단");
upperLeft.showInfo();
System.out.println("우측상단");
lowerRight.showInfo();
System.out.println();
}
}
public class Main5 {
public static void main(String[] args) {
Rectangle2 r = new Rectangle2(1, 1, 9, 9);
Rectangle2 cpy;
try {
cpy = (Rectangle2) r.clone();
r.changePos(2,2,2,7);
r.showPosition();
cpy.showPosition();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
이 부분을 변경해주었다 protected -> public
참조 값이 지니는 참조 값 그대로 새 인스턴스가 복사 -> 얕은 복사
Point 인스턴스까지 복사가 이뤄진 복사 -> 깊은 복사
Rectangle2 copy = (Rectangle2) super.clone();
Copy.upperLeft = (Point) upperLeft.clone(); -> 깊은 복사
인스턴스 변수가 String인 경우 깊은 복사.
String의 경우 Cloneable 인터페이스를 구현하지 않는다. String은 문자열 수정이 불가능 하므로, 깊은 복사 대상에서 제외해도 된다. 배열 또한 깊은 복사가 진행되지는 않는다.
Clone 메소드의 반환형 수정 -> Covariant Return Type
Clone 메소드는 반환형이 Object이다. 따라서 clone 메소드 호출시 동시에 형 변환.
<문제>
class Business implements Cloneable {
private String company;
private String work;
@Override
public Object clone() throws CloneNotSupportedException {
Business bs = (Business) super.clone();
return bs;
}
public Business(String company, String work) {
this.company = company;
this.work = work;
}
public void showBuismessInfo() {
System.out.println(company);
System.out.println(work);
}
}
class PersonInfo implements Cloneable {
private String name;
private int age;
private Business bz;
public PersonInfo(String name, int age, String company, String work) {
this.name = name;
this.age = age;
bz = new Business(company, work);
}
public void showPersonalInfo() {
System.out.println(name);
System.out.println(age);
bz.showBuismessInfo();
}
public PersonInfo clone() throws CloneNotSupportedException {
PersonInfo copy = (PersonInfo) super.clone();
copy.bz = (Business) bz.clone();
return copy;
}
}
public class Main6 {
public static void main(String[] args) {
try {
PersonInfo org
= new PersonInfo("James", 22, "SimpleSound", "enconding");
PersonInfo copy = org.clone();
org.showPersonalInfo();
copy.showPersonalInfo();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}