Object 클래스 - getClass() : 해당 클래스의 정보를 리턴한다.
basic.ex01 160-162
// 레퍼런스를 통해서 인스턴스의 클래스 정보를 알아낼 수 있다.
Class classInfo = obj1.getClass();
클래스 정보가 담겨있는 인스턴스 주소를 classinfo가 받는다.
System.out.println(classInfo.getName()); // 패키지명 + 바깥 클래스명 + 클래스명 //fully qualified name
System.out.println(classInfo.getSimpleName()); // 클래스명
170-174
Object 의 clone()
new Score() => 인스턴스를 만듬
복제방법 1. 직접 복제한다. 즉 새 객체를 만들어 기존 객체의 값을 저장한다.
복제방법 2. clone
protected 서브 클래스가 object의 clone 에 접근 가능
clone은 object의 protect 메서드임.
그래서 object의 서브 클래스가 clone 사용 가능
예외 던지는 건 try 안에 넣고 예외를 ㄹ받은 파라미터를 넣음
인스턴스를 복제할 수 있게 하려면, // => Object에서 제공하는 clone()을 호출할 수 있어야 한다. // => 그런데 clone()의 접근 범위가 protected 이라서 // 같은 패키지의 멤버이거나 서브 클래스가 아니면 호출할 수 없다. // 해결책? // => Object에서 상속 받은 clone()을 오버라이딩 한다. // => 다른 패키지의 멤버가 호출하려면 public 으로 접근 제어의 범위를 넓혀야 한다. // => 어떻게? 다음과 같이 오버라이딩 하라! @Override public String toString() { return "Score [name=" + name + ", kor=" + kor + ", eng=" + eng + ", math=" + math + ", sum=" + sum + ", aver=" + aver + "]"; } // => Object에서 상속 받은 clone()을 오버라이딩하여 다른 패키지의 멤버도 사용할 수 있게 // public 으로 접근 범위를 넓혀라! // => 오버라이딩은 접근 범위를 좁힐 수는 없지만, 넓힐 수는 있다. // => 오버라이딩 할 때 리턴 타입을 해당 클래스의 타입으로 변경해도 된다. @Override public Score clone() throws CloneNotSupportedException { // 복제를 위한 코드를 따로 작성할 필요가 없다. // JVM이 알아서 해준다. // 그냥 상속 받은 메서드를 오버라이딩하고, 접근 권한을 public 으로 확대한다. // 원래의 clone() 메서드를 실행한 다음에 // 리턴 타입을 해당 클래스로 형변환 한다. return (Score) super.clone(); } } |
☞clone 하면 =>JVM이 인스턴스 메모리 그대로 heap 복제 가능
s1에 200번지 들어가 있으면 s2에도 200번지 들어감,, !!
// 인스턴스 복제 기능을 활성화하려면 Cloneable 인터페이스를 구현해야 한다.
일반 상태에서는 비활성화에 protected으로 막혀있음 사용법을 정의한게 아니고 단순 활성화 비활성화를 표시하는 용도여서 그 안에 메서드 정의가 따로 없음. // => 이 인터페이스에는 메서드가 선언되어 있지 않다. // => 따라서 클래스는 따로 메서드를 구현할 필요가 없다. // => Cloneable을 구현하는 이유는 // JVM에게 이 클래스의 인스턴스를 복제할 수 있음을 표시하기 위함이다. // 이 표시가 안된 클래스는 JVM이 인스턴스를 복제해 주지 않는다. 즉 clone()을 호출할 수 없다. static class Score implements Cloneable { |
clone() : shallow copy(얕은 복제) vs deep copy(깊은 복제)
상황에 따라 맞는 copy 방식 따르기
String
// String 레퍼런스
// - String은 자바 기본 타입이 아니다. // - 클래스이다. String s1; // s1은 String 인스턴스 주소를 담는 레퍼런스이다. // String 인스턴스 // - 힙에 Hello 문자 코드를 저장할 메모리를 만들고 그 주소를 리턴한다. // - 내용물의 동일 여부를 검사하지 않고 무조건 인스턴스를 생성한다. // - 가비지가 되면 가비지 컬렉터에 의해 제거된다. s1 = new String("Hello"); String s2 = new String("Hello"); // 인스턴스가 같은지를 비교해보면, System.out.println(s1 == s2); // false => 서로 다른 인스턴스이다. 그래서 .equals 를 써야함 !!! |
문자열 리터럴
String s1 = new String("Hello"); // Heap 영역에 String 인스턴스를 만든다.
String s2 = "Hello"; // String Pool 영역에 String 인스턴스를 만든다. |
intern()
String s1 = new String("Hello"); // Heap 영역에 String 인스턴스를 생성한다.
intern() - String 객체에 들어 있는 문자열과 동일한 문자열을 갖고 있는 String 객체를 상수풀에서 찾는다. - 있으면 그 String 객체의 주소를 리턴한다. - 없으면 상수풀에 String 객체를 생성한 후 그 주소를 리턴한다. String s2 = s1.intern(); : s1의 문자열을 가지고 string 상수풀에 새 인스턴스를 생성한다. 단, 기존에 이미 같은 문자열의 인스턴 스가 있다면 기존객체 주소를 리턴한다. String s3 = "Hello"; // 해당 문자열을 가진 String 객체를 String Pool에서 찾는다. // 있으면 그 객체를 리턴한다. 없으면 새 객체를 만들고 리턴한다. System.out.println(s1 == s2);
System.out.println(s2 == s3); |
|
.equals
// equals()?
// - Object에 정의되어 있는 메서드이다.
// - 인스턴스가 같은지 비교한다.
// String의 equals()?
// - Object에서 상속 받은 것을 오버라이딩하였다.
// - 문자열이 같은지 비교한다.
// equals()는 대소문자를 구분한다.
System.out.println(s1.equals(s2));
// 대소문자 구분없이 문자열을 비교하고 싶다면,
System.out.println(s1.equalsIgnoreCase(s2));
StringBuffer는 Object에서 상속 받은 equals()를 오버라이딩 하지 않았다.
// StringBuffer 에 들어 있는 문자열을 비교하려면?
// - StringBuffer에서 String을 꺼내 비교하라!
//
// String s1 = b1.toString();
// String s2 = b2.toString();
// System.out.println(s1.equals(s2));
System.out.println(b1.toString().equals(b2.toString()));
string의 equals
인스턴스가 달라도 ,,, 문자열이랑 해시값이랑 같으면 같다고 나옴
String의 hashCode()은
// 문자열이 같으면 같은 hashCode()를 리턴하도록 오버라이딩 하였다.
// 이유?
// - 문자열이 같은 경우 같은 객체로 다루기 위함이다.
// - HashSet 에서 객체를 저장할 때 이 메서드의 리턴 값으로 저장 위치를 계산한다.
// - HashMap이나 Hashtable에서는 Key를 다룰 때 이 메서드의 리턴 값을 사용한다.
// - 보통 hashCode()와 equals()를 함께 오버라이딩 한다.
// String 클래스는 toString()도 오버라이딩 했다.
Object obj = new String("Hello");
// obj가 String 객체를 가리키더라도 // obj의 타입이 Object이기 때문에 Object에 선언한 멤버만 사용할 수 있다. // obj가 가리키는 원래 클래스의 메서드를 호출하고 싶다면 // 다음과 같이 원래 타입으로 형변환하라. String str = ((String) obj).toLowerCase(); // 기존의 것을 바꾸라는게 아니라 새로 만들어서 새 주소를 str에 담기 ! |
immutable 객체(불변)
어떤 객체를 호출하더라도 안 바뀜,,!
string 객체는 한번 값이 설정되면 절대 안 바뀜 ,, ! 그래서 toLowerCase()는 기존 것이 바뀌는게 아니구 새로 만드는것
// String 클래스의 메서드는 원본 인스턴스의 데이터를 변경하지 않는다.
// 다만 새로 String 객체를 만들 뿐이다. String s2 = s1.replace('l', 'x'); System.out.println(s1 == s2); System.out.printf("%s : %s\n", s1, s2); // 원본은 바뀌지 않는다. String s3 = s1.concat(", world!"); System.out.printf("%s : %s\n", s1, s3); // 원본은 바뀌지 않는다. -----------출력값 false
Hello : Hexxo Hello : Hello, world! |
원본을 바꾸고 싶으면 ,,!! -mutable 객체 StringBuffer
StringBuffer buf = new StringBuffer("Hello");
System.out.println(buf); buf.replace(2, 4, "xxxx");// 원본을 바꾼다. System.out.println(buf); Hello
Hexxxxo |
// Wrapper 클래스 - 종류
// 이렇게 primitive data type에 대응하여 만든 클래스를
primitive data를 포장하는 객체라고 해서 "랩퍼(wrapper) 클래스"라 부른다. // 래퍼 클래스의 주요 용도: => primitive data type의 값을 객체로 주고 받을 때 사용한다. => primitive data type의 값을 객체에 담아 전달하고 싶다면 언제든 wrapper 클래스의 인스턴스를 만들면 된다. Java 9 부터 wrapper 클래스의 생성자가 deprecated 상태이다. 가능한 생성자를 사용하여 인스턴스를 생성하지 말라! deprecated(비난받는, 유지보수가 중단되어, 사용이 권장되지 않는)? - 사용하지 않는 것이 좋다고 결정되었고, 가까운 장래에 제거될 것이라는 의미. // Wrapper 클래스의 인스턴스를 생성할 때는 생성자 대신 클래스 메서드를 사용하라. Byte b2 = Byte.valueOf((byte)100); Short s2 = Short.valueOf((short)20000); Integer i2 = Integer.valueOf(3000000); Long l2 = Long.valueOf(60000000000L); Float f2 = Float.valueOf(3.14f); Double d2 = Double.valueOf(3.14159); Boolean bool2 = Boolean.valueOf(true); Character c2 = Character.valueOf((char)0x41); } |
// wapper 클래스는 primitive type의 값을 객체로 다룰 수 있게 해준다.
// primitive type에 상관없이 Object 타입의 파라미터로 값을 받을 수 있다. static void m(Object value) { // 모든 객체를 받을 수 있다. System.out.printf("wrapper value=%s\n", value); } |
Wrapper 클래스 - 오토박싱(auto-boxing)/오토언박싱(auto-unboxing)
// 오토박싱
// - Java 1.5부터 지원하는 문법이다. // - 코드의 문맥에 따라 박싱(boxing)과 언박싱(unboxing)을 자동으로 수행한다. // // 즉 primitive data type 값을 Wrapper 클래스의 인스턴스에 바로 할당할 수 있다. // Integer obj = 100; // ==> Integer.valueOf(100) 컴파일러가 이렇게 바꿈 integer 객체의 주소가 obj에 할당됨 // obj는 레퍼런스인데 어떻게 가능한가? // => 내부적으로 Integer.valueOf(100) 호출 코드로 바뀐다. // => 즉 int 값이 obj에 바로 저장되는 것이 아니라, // 내부적으로 Integer 객체가 생성되어 그 주소가 저장된다. // => 이렇게 int 값을 자동으로 Integer 객체로 만드는 것을 // "오토박싱(auto-boxing)"이라 한다. |
// 오토 언박싱
// - Java 1.5부터 지원하는 문법이다.
// - 코드의 문맥에 따라 박싱(boxing)과 언박싱(unboxing)을 자동으로 수행한다. // // 즉 Wrapper 객체의 값을 primitive data type 변수에 직접 할당할 수 있다. // Integer obj = Integer.valueOf(300); int i = obj; // ==> obj.intValue() => 값을 꺼내 i에 저 // obj에 저장된 것은 int 값이 아니라 Integer 객체의 주소인데 어떻게 가능한가? // => 내부적으로 obj.intValue() 호출 코드로 바뀐다. // => 즉 obj에 들어있는 인스턴스 주소가 i에 저장되는 것이 아니라, // obj 인스턴스에 들어 있는 값을 꺼내 i에 저장하는 것이다. // => 이렇게 Wrapper 객체 안에 들어 있는 값을 자동으로 꺼낸다고 해서 // "오토언박싱"이라 부른다. |
public class Exam0223 {
public static void main(String[] args) { int i1 = 100; Integer obj = Integer.valueOf(200); printInt(obj); // 컴파일러가 printInt(obj.intValue())로 바꾼다. // 즉 "오토 언박싱"을 수행한다. printObject(i1); // 컴파일러가 printObject(Integer.valueOf(100)) 으로 바꾼다. // 즉 "오토 박싱"을 수행한다. } static void printInt(int value) { System.out.println(value); } static void printObject(Integer obj) { System.out.println(obj.toString()); } } ==========출력값
200
100 |
|
// 설명:
// => 정수 값이 -128 ~ 127 범위일 경우 // 자주 사용되는 수이기 때문에 // String 리터럴처럼 상수 풀에 Integer 객체를 생성한다. 범위 넘어가면 heap에 생성됨 상수풀에 만들어지면 주소 같다고 나오는데 heap 만들어지는건 매번 새로 만들기 때문에 다르다고 함. // 결론!
// - wrapper 객체의 값을 비교할 때 == 연산자를 사용하지 말라! // - -128 ~ 127 범위 내의 값이라면 == 연산자를 사용하여 비교할 수도 있지만, // 매번 비교할 때 마다 범위의 유효성을 생각하는 것이 번거롭다. // - 그냥 equals() 메서드를 사용하여 값을 비교하라! // - 더 좋은 방법은 auto-unboxing 하여 primitive type 의 값으로 바꾼후에 == 연산자로 비교하라. |
'[네이버클라우드] 클라우드 기반의 개발자 과정 7기 > 웹프로그래밍' 카테고리의 다른 글
[NC7기-44일차(6월27일)] - 웹프로그래밍 25일차 (0) | 2023.06.27 |
---|---|
[NC7기-43일차(6월26일)] - 웹프로그래밍 24일차 (0) | 2023.06.26 |
[NC7기-42일차(6월23일)] - 웹프로그래밍 23일차 (0) | 2023.06.23 |
[NC7기-41일차(6월22일)] - 웹프로그래밍 22일차 (0) | 2023.06.22 |
[NC7기-40일차(6월21일)] - 웹프로그래밍 21일차 (0) | 2023.06.21 |