API
: Application Programming Interface
: API는 라이브러리(library)라고 부르기도 하는데, 프로그램 개발에 자주 사용되는 클래스 및 인터페이스의 모음을 말한다.
API 도큐먼트
: 쉽게 API를 찾아 이용할 수 있도록 문서화한 것
: HTML 페이지로 작성되어 URL로 접속이 가능하다 -> API 도큐먼트 URL
자바 애플리케이션을 개발할 때 공통적으로 가장 많이 사용하는 패키지일 것이다.
: 자바 프로그램의 기본적인 클래스를 담고 있는 패키지
: java.lang 패키지에 있는 클래스와 인터페이스는 import 없이 사용 가능
주요 클래스와 용도를 간략히 보자
자바 프로그램 개발에 조미료 같은 역할을 하는 클래스를 담고있다. 몇가지 보자면 이렇다.
Object는 자바의 최상위 부모 클래스에 해당한다. Object 클래스는 필드가 없고, 메소드들로 구성되어있다.
// static이 붙은 애들은
// 클래스명.메소드명
// 으로 사용
String name = "홍길동";
System.out.println(Objects.hashCode(name));
// isNull은 boolean 형이고 name은 값이 있으니까 출력시 false
System.out.println(Objects.isNull(name));
// Math 수학
// sqrt = 제곱근
System.out.println(Math.max(3.4f, 8.5f));
System.out.println(Math.sqrt(9.0));
System.out.println("--------------------------------------");
// 반지름을 입력받아 원의 넓이 구하기
Scanner sc = new Scanner(System.in);
System.out.println("반지름 입력해주시오");
System.out.print(">>");
double radius = sc.nextDouble();
double result = (radius * radius * Math.PI);
System.out.println("원의 넓이 : " + result);
System.out.println("--------------------------------------");
// 참조변수에서의 ==
// 은 주소를 비교
Object obj1 = new Object();
Object obj2 = new Object();
boolean result1 = (obj1 == obj2);
System.out.println(result1);
// 기본 변수에서의 ==
// 은 값 크기 자체를 비교
int a = 10, b = 10;
boolean result2 = (a == b);
System.out.println(result2);
// 기본 변수
String name1 = "홍길동", name2 = "홍길동";
boolean result3 = (name1 == name2);
System.out.println(result3);
// 참조 변수
String name3 = new String("홍길동");
String name4 = new String("홍길동");
boolean result4 = (name3 == name4);
System.out.println(result4);
System.out.println("-------------------");
// 객체 비교 equals
// 값이 따로 없고 가지고 있는 주소가 다르니까 false
result1 = obj1.equals(obj2);
System.out.println(result1);
// 가지고 있는 "내용"이 같으니까 주소 상관없이 true
result3 = name1.equals(name2);
System.out.println(result3);
result4 = name3.equals(name4);
System.out.println(result4);
// ia라는 변수에 Integer클래스 객체화해 담고 10 넣어주기
// 그냥 int형으로는 equals()가 사용안되니까
// 정수형에 관련된 메소드를 사용하기 위해..
Integer ia = new Integer(10);
Integer ib = new Integer(10);
result2 = ia.equals(ib);
System.out.println(result2);
result2 = ia.equals(b);
System.out.println(result2);
: Object 클래스의 equals() 메소드는 비교 연산자인 ==과 동일한 결과를 리턴한다.
: 두 객체를 비교해서 논리적으로 동등하면 true를 리턴하고, 그렇지 않으면 false를 리턴한다. Object의 equals() 메소드는 직접 사용되지 않고 하위 클래스에서 재정의하여 비교할 때 이용된다.
-> 예를 들어, String 클래스의 경우 메소드를 재정의해서 번지 비교가 아닌 문자열 비교로 변경했다. 따라서 객체의 번지를 비교하는 것이 아니고, 문자열이 동일한지 조사해서 같다면 true를 리턴한다.
Object obj1 = new Object();
Object obj2 = new Object();
// 두 result는 결과가 동일하다
boolean result = obj1.equals(obj2);
boolean result = (obj1 == obj2);
// String의 재정의된 equals는 문자열이 동일한지 조사 후 값을 리턴한다
String str1 = "홍길동";
String str2 = "홍길동";
boolean result = str1.equals(str2);
equals() 메소드를 재정의할 때에는 매개값이(비교 객체가) 기준 객체와 동일한 타입의 객체인지 먼저 확인해야 한다.
사용 모습 보기
public class Member {
public String id;
public Member(String id) {
this.id = id;
}
@Override
public boolean equals(Object obj) {
// 매개값이 Member 타입인지 확인
if (obj instanceof Member) {
// 다운 캐스팅하여
// Object 클래스를 Member 클래스로 변경
Member member = (Member) obj;
// Member 타입으로 강제 타입변환하고
// id 필드 값이 동일한지 검사한 후
// 동일하다면 true 리턴
if (id.equals(member.id)) {
return true;
}
}
// 매개값이 Member 타입이 아니거나
// id 필드 값이 다른 경우 false리턴
return false;
}
}
객체를 식별할 하나의 정수값을 말한다. Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴하기 때문에 객체마다 다른 값을 가지고 있다. 논리적 동등 비교시 오버라이딩할 필요가 있다.
해시코드 값이 다르면 다른 객체로 판단하고, 해시코드 값이 같으면 equals() 메소드로 한번 더 비교한다.
동등객체 판단하는 기준
1단계 : hashCode()를 재정의 -> 동등객체인지 판단하려는 필드의 해쉬코드 구함
2단계 : equals() 재정의해 -> 동등객체인지 판한하려는 필드를 조건문으로 판단
public class Key {
public int number;
public Key(int number) {
this.number = number;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Key) {
System.out.println("key의 equals 부분");
Key compareKey = (Key) obj;
if (this.number == compareKey.number) {
return true;
}
}
return false;
}
// Object의 해쉬코드는 주소를 리턴
// 주소는 (ex) 같은 100 번지라도 밑으로 줄줄이 다른 주소로 들어감
// 오버라이딩한 해쉬코드는 해당 번지에 있는걸
// 꺼내오겠
@Override
public int hashCode() {
System.out.println("해쉬코드 부분");
return number;
}
}
// 힙 메모리 올릴 때 주소를 만들어 내는 것은 hashCode()
public class KeyEx {
public static void main(String[] args) {
// Key 객체를 식별키로 사용해서
//String 값을 저장하는 HashMap 객체 생성
// < 클래스, 타입 >
HashMap<Key, String> hashMap = new HashMap<Key, String>();
// 식별키 "new Key(1)" 로 "홍길동" 을 저장
// Key 라고 되어있는 클래스를 객체화하고
// 주소에 값을 "홍길동이라" 넣음
System.out.println("1--------------");
hashMap.put(new Key(1), "홍길동");
System.out.println("2--------------");
// 식별키 "new Key(1)"로 홍길동을 읽어옴
String value = hashMap.get(new Key(1));
System.out.println("3--------------");
System.out.println(value);
}
}
결론적으로 말해서 객체의 동등 비교를 위해서응 Object의 equals() 메소드만 재정의하지 말고 hashCode() 메소드도 재정의해, 논리적 동등 객체일 경우 동일한 해시코드가 리턴되도록 해야 한다.
객체의 문자 정보를 리턴한다. 객체명만 입력해도 자동으로 toString()이 생략되었다 간주한다.
Object obj1 = new Object();
Date obj2 = new Date();
// 객체명만 입력해도 자동으로 toString()이 생략되었다 간주
System.out.println(obj1.toString());
System.out.println(obj2);
객체 복제
: 원본 객체의 필드값과 동일한 값을 가지는 새로운 객체를 생성하는 것을 말합니다. 객체를 복제하는 이유는 신뢰하지 않는 영역으로 원본 객체를 넘겨 작업 시 훼손될 수 있기 때문에, 원본 객체를 안전하게 보호하기 위해서이다.
: 단순히 필드값을 복사해 객체를 복제하는 것을 말한다.
: 필드가 기본 타입일 경우 값 복사가 되고, 필드가 참조타입일 경우에는 객체의 번지가 복사된다.
: implements Cloneable이 있어야 복제를 허용한다는 표시
-> 클래스 설계자가 복제를 호용하지 않는다면 이 Cloneable 인터페이스를 구현하지 않으면 된다
public class Cat implements Cloneable {
// 필드
int catA = 10;
Catcat cc = null;
// 기본 생성자
void catMethod() { }
// 복제하는 곳
Cat getCloned() {
Cat cloned = null;
try {
cloned = (Cat) clone();
} catch (CloneNotSupportedException e) { }
return cloned;
}
public Cat() {
cc = new Catcat();
}
}
class Catcat { }
2) 메인.
public class CatEx {
public static void main(String[] args) {
Cat original = new Cat();
// clone 메소드 호출
// 얕은 복제하기
Cat clond = original.getCloned();
// 복제본 출력하기
clond.catA = 20;
System.out.println(clond.catA);
System.out.println(clond.cc);
// 원본 출력하기
System.out.println(original.catA);
System.out.println(original.cc);
}
}
실행결과
얕은 복제일때 배열은
실제 다른 공간이 만들어지는 게 아니라 주소만 복사된다.
그래서 오리지널에서 바꿔도 원본이 수정되고
복제에서 바꿔도 원본이 수정된다.
배열은 일반 객체와 동일한 형태로 얕은 복사됨
// 얕은 복제
// Cloneabe 은 복제가 가능한 클래스라는 표시이다
public class Member implements Cloneable {
// 멤버필드
public String id;
public String name;
public String password;
public int age;
public boolean adult;
// 생성자
public Member(String id, String name, String password, int age, boolean adult) {
this.id = id;
this.name = name;
this.password = password;
this.age = age;
this.adult = adult;
}
// 배열
public int intAr[] = {1,2,3};
// 얉은 복제를 해주는 메소드
public Member getMember() {
// 변수 선언
Member cloned = null;
try {
// clone() 메소드의 리턴 타입은 Object 이므로
// Member 타입으로 캐스팅 해주어야 함
cloned = (Member) clone();
} catch (CloneNotSupportedException e) { }
// 복제한 주소 리턴해줌
return cloned;
}
}
2)
public class MemberEx {
public static void main(String[] args) {
//원본 객체 생성
Member original = new Member("blue", "홍길동", "12345", 25, true);
//복제 객체를 얻은 후에 패스워드 변경
// 얕은 복제해주기
Member cloned = original.getMember();
cloned.password = "67890";
// 배열 수정시 같은 주소를 사용하기 때문에
// original이랑 cloned 모두 값이 변경됨
cloned.intAr[1] = 10;
original.intAr[2] = 20;
System.out.println("주소 찍기 : " + cloned);
System.out.println("[복제 객체의 필드값]");
System.out.println("id: " + cloned.id);
System.out.println("name: " + cloned.name);
System.out.println("password: " + cloned.password);
System.out.println("age: " + cloned.age);
System.out.println("adult: " + cloned.adult);
System.out.println("배열 주소 : " + cloned.intAr);
System.out.println("배열[1], [2] : " + cloned.intAr[1] + ", " + cloned.intAr[2]);
System.out.println();
System.out.println("주소찍기 : " + original);
System.out.println("[원본 객체의 필드값]");
System.out.println("id: " + original.id);
System.out.println("name: " + original.name);
System.out.println("password: " + original.password);
System.out.println("age: " + original.age);
System.out.println("adult: " + original.adult);
System.out.println("배열 주소 : " + original.intAr);
System.out.println("배열[1],[2] : " + original.intAr[1] + ", " + original.intAr[2]);
}
}
실행결과
: 얕은 복제는 참조타입 필드의 경우 번지만 복제되기 때문에 원본 객체의 필드와 복제 객체의 필드는 같은 객체를 참조하게 된다. 따라서 복제 객체에서 참조 객체를 변경시 그대로 원본 객체도 수정된 객체를 가지게 된다(위 얕은 복제 예시 중 두번째처럼)
: 깊은 복제의 순서
1. 얕은 복제 -> 필드가 복제 된다
2. 배열이 있으면 배열 복제
3. 객체가 있다면 객체 복제
1) car 클래스
public class Car {
public String model;
public Car(String model) {
this.model = model;
}
}
public class Deep_Clone implements Cloneable {
public String name;
public int age;
public int[] scores;
public Car car;
public Deep_Clone(String name, int age, int[] scores, Car car) {
this.name = name;
this.age = age;
this.scores = scores;
this.car = car;
}
// 깊은 복제를 하기 위해서 Object 클래스의 clone() 메소드를 재정의 합니다
@Override
protected Object clone() throws CloneNotSupportedException {
// 1. 먼저 얕은 복사를 해서 name, age를 복제한다.
Deep_Clone cloned = (Deep_Clone) super.clone();
// 2. 배열 scores를 복제한다.
// 새로운 배열 = Arrays.copyof(원본 배열, 원본 배열에서 복사하고 싶은 요소들의 길이)
// scores에 있는 자료를 scores.length 만큼 복제해라
cloned.scores = Arrays.copyOf(this.scores, this.scores.length);
// 3. car를 깊은 복제한다.
cloned.car = new Car(this.car.model);
// 깊은 복제된 Member 객체가 담긴 cloned 리턴
return cloned;
}
public Deep_Clone getMember() {
Deep_Clone cloned = null;
try {
cloned = (Deep_Clone) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return cloned;
}
}
public class MemberEx {
public static void main(String[] args) {
// 원본 객체 생성
Deep_Clone original = new Deep_Clone("홍길동", 25, new int[] { 90, 90 }, new Car("소나타"));
// 복제 객체를 얻은 후에 참조 객체의 값을 변경
Deep_Clone cloned = original.getMember();
cloned.scores[0] = 100;
cloned.car.model = "그랜져";
cloned.name = "나헤석";
System.out.println("[복제 객체의 필드값]");
System.out.println("name: " + cloned.name);
System.out.println("age: " + cloned.age);
System.out.print("scores: {");
for(int i=0; i<cloned.scores.length; i++) {
System.out.print(cloned.scores[i]);
System.out.print((i==(cloned.scores.length-1))?"":",");
}
System.out.println("}");
System.out.println("car: " + cloned.car.model);
System.out.println();
// 두번째 복제
Deep_Clone cloned2 = original.getMember();
cloned2.name = "김자바";
cloned2.age = 25;
cloned2.scores[0] = 100;
cloned2.scores[1] = 100;
cloned2.car.model = "벤츠";
System.out.println("[복제 객체 2의 필드값]");
System.out.println("name: " + cloned2.name);
System.out.println("age: " + cloned2.age);
System.out.print("scores: {");
for(int i=0; i<cloned2.scores.length; i++) {
System.out.print(cloned2.scores[i]);
System.out.print((i==(cloned2.scores.length-1))?"":",");
}
System.out.println("}");
System.out.println("car: " + cloned2.car.model);
System.out.println();
// 원본
System.out.println("[원본 객체의 필드값]");
System.out.println("name: " + original.name);
System.out.println("age: " + original.age);
System.out.print("scores: {");
for(int i=0; i<original.scores.length; i++) {
System.out.print(original.scores[i]);
System.out.print((i==(original.scores.length-1))?"":",");
}
System.out.println("}");
System.out.println("car: " + original.car.model);
}
}