11-1. Object 클래스
모든 클래스의 최상위 클래스
java.lang 패키지
- 프로그래밍 시 import 하지 않아도 자동으로 import 됨
- import.java.lang.*;
- 많이 사용하는 기본 클래스들이 속한 패키지
- String, integer, System...
모든 클래스는 Object 클래스를 상속 받는다
- java.lang.Object 클래스
- 모든 클래스의 최상위 클래스는 Object 클래스
- 모든 클래스는 Object에서 상속받고, Object 클래스의 메서드 중 일부는 재정의해서 사용할 수 있음
- 컴파일러가 extends Object를 추가함
class Student -> class Student extends Object
㉠ toString() 메서드
- 어떤 객체를 문자화 시켜주는 메소드 (인스턴스 정보를 문자열로 반환하는 메소드)
- Object의 멤버 (자식)
- overriding 통해 구체적 내용 재정의하면 객체의 참조 변수를 이용해 원하는 문자열을 표현할 수 있음
package ch01;
public class Book { int bookNumber; String bookTitle; Book(int bookNumber, String bookTitle) { // 책 번호와 제목을 매개변수로 입력받는 생성자 this.bookNumber = bookNumber; this.bookTitle = bookTitle; } @Override // toString() 메서드 재정의 public String toString() { return bookTitle + "," + bookNumber; } } |
package ch01;
public class ToStringEx { public static void main(String[] args) { Book book1 = new Book(200, "개미"); System.out.println(book1); System.out.println(book1.toString()); } } |
㉡ equals() 메서드
- 두 인스턴스의 주소 값을 비교하여 true / false를 반환 (물리적으로 같다 = 주소 값이 같다)
// 물리적으로 같다 (두 인스턴스의 주소 값이 같다 = 두 변수가 같은 메모리 주소를 가리키고 있다)
Student studentLee = new Student(100, "이상원"); Student studentLee2 = studentLee; // 주소 복사 |
- 재정의 하여 두 인스턴스가 논리적으로 동일함 (서로 다른 주소지만 저장된 정보는 같다) 의 여부를 구현함
// 논리적으로 같다
Student studentLee = new Student(100, "이상원"); Student studentLee2 = studentLee; Student studentSang = new Student(100, "이상원"); |
- 인스턴스가 다르더라도 논리적으로 동일한 경우 true를 반환하도록 재정의 할 수 있음 (같은 학번, 같은 사번, 같은 아이디의 회원...)
- Object obj
package ch02;
public class Student { int studentId; String studentName; public Student(int studentId, String studentName) { this.studentId = studentId; this.studentName = studentName; } public String toString() { return studentId + "," + studentName; } } |
package ch02;
public class EqualsTest { public static void main(String[] args) { Student studentLee = new Student(100, "이상원"); Student studentLee2 = studentLee; // 주소 복사 Student studentSang = new Student(100, "이상원"); // 동일한 주소의 두 인스턴스 비교 if(studentLee == studentLee2) // == 기호로 비교 System.out.println("studentLee와 studentLee2의 주소는 같습니다."); else System.out.println("studentLee와 studentLee2의 주소는 다릅니다."); if(studentLee.equals(studentLee2)) // equals() 메소드로 비교 System.out.println("studentLee와 studentLee2는 동일합니다."); else System.out.println("studentLee와 studentLee2는 동일하지 않습니다."); // 동일인이지만 인스턴스의 주소가 다른 경우 if(studentLee == studentSang) // == 기호로 비교 System.out.println("studentLee와 studentSang의 주소는 같습니다."); else System.out.println("studentLee와 studentSang의 주소는 다릅니다."); if(studentLee.equals(studentSang)) // equals() 메소드로 비교 System.out.println("studentLee와 studentSang은 동일합니다."); else System.out.println("studentLee와 studentSang은 동일하지 않습니다."); } } |
studentLee와 studentLee2의 주소는 같습니다.
studentLee와 studentLee2는 동일합니다. studentLee와 studentSang의 주소는 다릅니다. studentLee와 studentSang은 동일하지 않습니다. |
Object의 equals() 메서드의 원래 기능은 두 인스턴스의 주소를 비교하고, 같은 주소인 경우만 결과가 true가 된다.
그러므로 이 예제에서 studentLee 참조변수와 studentLee2 참조 변수는 동일한 주소를 가리키므로 true이고,
studentSang의 경우는 다른 주소를 가리키므로 false 이다.
하지만
쇼핑몰 회원의 회원 아이디가 같으면 무조건 같은 회원인 것처럼
인스턴스의 주소가 달라도 동일한 객체임을 확인할 수 있어야 한다!
이럴 때, equals() 메서드를 재정의하여 주소가 다르더라도 논리적으로 같은 인스턴스인지 확인하도록 구현할 수 있어야 한다.
package ch02;
public class Student { int studentId; String studentName; public Student(int studentId, String studentName) { this.studentId = studentId; this.studentName = studentName; } public String toString() { return studentId + "," + studentName; } // 재정의 @Override public boolean equals(Object obj) { if(obj instanceof Student) { //obj 인스턴스 자료형이 Student형이라면 Student std = (Student)obj; // 인스턴스 obj를 Student형으로 다운캐스팅 if(this.studentId == std.studentId) //내가 가진 id와 받은 id가 같다면 return true; else return false; } return false; } } |
재정의하여 값을 출력 결과
주소는 다르지만 동일하다고 나온다.
studentLee와 studentLee2의 주소는 같습니다.
studentLee와 studentLee2는 동일합니다. studentLee와 studentSang의 주소는 다릅니다. ★★ studentLee와 studentSang은 동일합니다. |
㉢ hashCode() 메서드
- hashCode()는 인스턴스의 저장 주소를 반환함
- 힙메모리에 인스턴스가 저장되는 방식이 hash 방식
- hash : 정보를 저장, 검색하는 자료구조
- 자료의 특정 값(키 값)에 대한 저장 위치를 반환해주는 해시 함수를 사용
- 두 인스턴스가 같다는 것은?
두 인스턴스에 대한 equals() 반환 값이 true 동일한 hashCode() 값을 반환
- 논리적으로 동일함을 위해 equals() 메서드를 재정의 하였다면 hashCode() 메서드도 재정의하여 동일한 hashCode 값이 반환되도록 한다.
package ch02;
public class Student { int studentId; String studentName; public Student(int studentId, String studentName) { this.studentId = studentId; this.studentName = studentName; } public String toString() { return studentId + "," + studentName; } // 재정의 @Override public boolean equals(Object obj) { if(obj instanceof Student) { //obj 인스턴스 자료형이 Student형이라면 Student std = (Student)obj; // 인스턴스 obj를 Student형으로 다운캐스팅 if(this.studentId == std.studentId) //내가 가진 id와 받은 id가 같다면 return true; else return false; } return false; } // 해시 코드 값으로 학번을 반환 하도록 메서드 재정의 @Override public int hashCode() { return studentId; // equals()메서드에서 논리적으로 같다는 것을 구현할 때 사용한 } // 멤버변수를 활용하는 것이 좋다. } |
㉣ clone() 메서드
- 객체의 원본을 복제하는데 사용하는 메서드
- 생성과정의 복잡한 과정을 반복하지 않고 복제 할 수 있음
- clone() 메서드를 사용하면 객체의 정보(멤버 변수 값 등..)가 동일한 또 다른 인스턴스가 생성되는 것이므로, 객체 지향 프로그램에서의 정보 은닉, 객체 보호의 관점에서 위배될 수 있음
- 해당 클래스의 clone() 메서드의 사용을 허용한다는 의미로 cloneable 인터페이스를 명시해 줌
package ch03;
public class Point { int x; int y; Point(int x, int y) { this.x = x; this.y = y; } public String toString() { return "x =" + x + "," + "y = " + y; } } |
package ch03;
public class Circle implements Cloneable { // 객체를 복제해도 된다는 의미로 Cloneable 인터페이스를 함께 선언 Point point; int radius; Circle(int x, int y, int radius) { this.radius = radius; point = new Point(x, y); } public String toString() { return "원점은 " + point + "이고," + "반지름은 " + radius + "입니다."; } @Override protected Object clone() throws CloneNotSupportedException { //오류를 예외처리 return super.clone(); } } |
package ch03;
public class ObjectCloneTest { public static void main(String[] args) throws CloneNotSupportedException { Circle circle = new Circle(10, 20, 30); // clone() 메서드를 사용해 circle 인스턴스를 copyCircle에 복제함 Circle copyCircle = (Circle)circle.clone(); System.out.println(circle); System.out.println(copyCircle); System.out.println(System.identityHashCode(circle)); System.out.println(System.identityHashCode(copyCircle)); } } |
11-2. String 클래스
- String 선언하는 두 가지 방법
String str1 = new String("abc"); // 생성자의 매개변수로 문자열 생성 ㅡ> 힙 메모리에 인스턴스로 생성되는 경우 String str2 = "test"; // 문자열 상수를 가리키는 방식 ㅡ> 상수 풀(constant pool)에 있는 주소 사용 |
- 힙 메모리는 생성될 때 마다 다른 주소 값을 가지지만, 상수 풀의 문자열은 모두 같은 주소 값을 가짐
- 한번 생성된 String은 불변 (immutable)
String 클래스의 final char[ ] 변수
- String을 연결하면 기존의 String에 연결되는 것이 아닌 새로운 문자열이 생성됨 ㅡ> 메모리 낭비 발생
package ch04;
public class StringTEST { public static void main(String[] args) { // 두 문자열 연결하는 예제 String javaStr = new String("java"); String androidStr = new String("android"); System.out.println(javaStr); System.out.println("처음 문자열 주소 값 : " + System.identityHashCode(javaStr)); // 문자열 javaStr과 androidStr을 연결하여 javaStr에 대입 javaStr = javaStr.concat(androidStr); // 두 문자열을 연결하는 메소드 concat() System.out.println(javaStr); System.out.println("연결된 문자열 주소 값 : " + System.identityHashCode(javaStr)); } } |
java
처음 문자열 주소 값 : 2083562754 javaandroid 연결된 문자열 주소 값 : 1239731077 |
문자열은 불변(immutable)하므로 javaStr 변수 값 자체가 변하는 것이 아니라
새로운 문자열이 생성된 것을 알수 있다.
StringBuilder, StringBuffer 활용하기
- 내부적으로 가변적인 char[ ] 를 멤버 변수로 가짐
- 문자열을 여러번 연결하거나 변경할 때 사용하면 유용함
- 새로운 인스턴스를 생성하지 않고 char[ ]를 변경함 ㅡ> 추가 메모리 사용하지 않아도 됨
- StringBuffer는 멀티 쓰레드 프로그램에서 동기화(synchronization)을 보장 (문자열이 안전하게 변경되도록 보장)
- 단일 쓰레드 프로그램에서는 StringBuilder 사용을 권장 (실행속도가 빠르기 때문)
package ch04;
public class StringBuilderTest { public static void main(String[] args) { String javaStr = new String("Java"); System.out.println("javaStr 문자열 주소 :" + System.identityHashCode(javaStr)); //인스턴스가 처음 생성됐을때 메모리 주소 StringBuilder buffer = new StringBuilder(javaStr); // StringBuilder 생성 System.out.println("연산 전 bufffer 메모리 주소:" + System.identityHashCode(buffer)); //문자열 추가 append() buffer.append(" and"); buffer.append(" android"); buffer.append(" programming is fun!!!"); System.out.println("연산 후 buffer 메모리 주소:" + System.identityHashCode(buffer)); javaStr = buffer.toString(); //String 클래스로 반환 System.out.println(javaStr); System.out.println("새로 만들어진 javaStr 문자열 주소 :" + System.identityHashCode(javaStr)); } } |
javaStr 문자열 주소 :2083562754
연산 전 bufffer 메모리 주소:1239731077 연산 후 buffer 메모리 주소:1239731077 Java and android programming is fun!!! 새로 만들어진 javaStr 문자열 주소 :557041912 |
전과 후의 메모리 주소가 같다.
ㅡ> append() 메서드가 실행될 때 마다 메모리가 새로 생성되는 것이 아니라, 하나의 메모리에 계속 연결된다 !
text block 사용하기 (java 13에서 프리뷰로 추가되고 15에서 정식 발표됨)
- 문자열을 """ """ 사이에 이어서 만들 수 있음
- html. jsom 문자열을 만드는데 유용하게 사용할 수 있음
11-3. Class 클래스
- 자바의 모든 클래스와 인터페이스는 컴파일 후 class 파일이 생성됨
- Class 클래스는 컴파일 된 class 파일을 로드하여 객체를 동적 로드하고, 정보를 가져오는 메서드가 제공됨
- Class.forName("클래스 이름") 메서드로 클래스를 동적으로 로드 함
Clss c = Class.forName("java.lang.String"); |
- 클래스 이름으로 직접 Class 클래스 가져오기
Class c = String.class; |
- 생성된 인스턴스에서 Class 클래스 가져오기
String s = new String(); Class c = s.getClass(); // Object 메서드 |
동적 로딩
- 컴파일 시에 데이터 타입이 binging 되는 것이 아닌, 실행(runtime) 중에 데이터 타입을 binding 하는 방법
- 프로그래밍 시에는 문자열 변수로 처리했다가 런타임시에 원하는 클래스를 로딩하여 binding 할 수 있다는 장점
- java.lang.reflect 패키지에 있는 클래스를 활용하여 프로그래밍
- 일반적으로 자료형을 알고 있는 경우엔 사용하지 않음
- 자바에서는 Class.forName() 메서드를 동적 로딩으로 제공
클래스 정보 알아보기
- reflection 프로그래밍 : Class 클래스를 사용하여 클래스의 정보(생성자, 변수, 메서드) 등을 알 수 있고, 인스턴스를 생성하고, 메소드를 호출하는 방식의 프로그래밍
- 로컬 메모리에 객체 없는 경우, 원격 프로그래밍, 객체의 타입을 알 수 없는 경우에 사용
- java.lang.reflect 패키지에 있는 클래스를 활용하여 프로그래밍
- 일반적으로 자료형을 알고 있는 경우엔 사용하지 않음
'Programming Language > JAVA' 카테고리의 다른 글
[Do it 자바 프로그래밍 입문] 13. 컬렉션 프레임워크 (0) | 2023.06.14 |
---|---|
[Do it 자바 프로그래밍 입문] 12. 자료구조 (1) | 2023.06.13 |
추상클래스(abstract)와 인터페이스(interface) 차이점 (0) | 2023.06.01 |
기본 이론(1) (0) | 2023.05.28 |
인터페이스(interface) (0) | 2023.05.25 |