본문 바로가기

Java-Study

Java Stduy : 7주차 정리 - 기본 클래스(Object, String, Wrapper, Class 클래스)

Object 클래스

  • Object 클래스는 모든 자바 클래스의 최상위 클래스이다.
  • 모든 클래스는 Object 클래스로부터 상속을 받는데 클래스 선언시 자동으로 쓰이게 되기 때문에 따로 쓰지는 않는다.

toString() 메서드

  • 객체 정보를 문자열로 표현하여 반환한다.
  • 재정의하여 객체에 대한 설명이나 특정 멤버 변수 값을 반환한다.
package object;

class Book{

    int bookNumber;
    String bookTitle;

    Book(int bookNumber, String bookTitle){ //번호와 책을 매개변수로 입력받는 생성자
        this.bookNumber = bookNumber;
        this.bookTitle = bookTitle;
    }
}

public class ToStringEx {

    public static void main(String[] args) {

        Book book1 = new Book(200, "개미");

        System.out.println(book1);  //인스턴스 정보(클래스 이름, 주소 값)
        System.out.println(book1.toString());   //toString() 메서드로 인스턴스 정보를 보여줌
    }
}

toString() 메서드 재정의 하기

package object;

class Book{

    int bookNumber;
    String bookTitle;

    Book(int bookNumber, String bookTitle){ //번호와 책을 매개변수로 입력받는 생성자
        this.bookNumber = bookNumber;
        this.bookTitle = bookTitle;
    }

    @Override
    public String toString() {  //toString() 메서드 재정의

        return bookTitle + "," + bookNumber;
    }

}

public class ToStringEx {

    public static void main(String[] args) {

        Book book1 = new Book(200, "개미");

        System.out.println(book1);  //인스턴스 정보(클래스 이름, 주소 값)
        System.out.println(book1.toString());   //toString() 메서드로 인스턴스 정보를 보여줌
    }
}

 

equals() 메서드

  • 두 인스턴스의 주소 값을 비교하여 boolean 값을 반환해 준다.
  • 논리적 동일성(논리적으로 두 인스턴스가 같음)을 구현할 때도 재정의 하여 사용
    • 서로 다른 주소 값을 가질 때도 같은 인스턴스라고 정의할 수 있는 경우가 있기 때문
package object;

class Student{

    int studentId;
    String studentName;

    public Student(int studentId, String studentName){
        this.studentId = studentId;
        this.studentName = studentName;
    }

    public String toString(){
        return studentId + "," + studentName;
    }
}

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는 동일하지 않습니다.");

        //주소는 다르지만 equals 의 결과가 true 인 경우
        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은 동일하지 않습니다.");
    }
}

위 결과처럼  equals() 메서드를 사용하여 인스턴스의 주소는 다르지만 같은 인스턴스라고 정의할 수 있다.

 

equals() 메서드 재정의하기

package object;

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) { //equals() 메서드 재정의
        if(obj instanceof Student){
            Student std = (Student)obj;
            if( studentId == std.studentId) //재정의한 equals() 메서드는 학생의 학번이 같으면 true 반환
                return true;
            else return false;
        }
        return false;
    }

}

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는 동일하지 않습니다.");

        //주소는 다르지만 equals 의 결과가 true 인 경우
        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은 동일하지 않습니다.");
    }
}

 

hashCode() 메서드

  • 해시(hash)는 정보를 저장하거나 검색할 때 사용하는 자료구조이다.
  • 정보를 어디에 저장할 것인지, 어디서 가져올 것인지 해시 함수를 사용하여 구현한다.
  • 해시 함수는 객체의 특정 정보(키 값)를 매개변수 값으로 넣으면 그 객체가 저장되어야 할 위치나
    저장된 해시 테이블 주소를 반환한다.
package object;

import java.util.Date;

public class HashCodeTest {

    public static void main(String[] args) {

        String str1 = new String("abc");
        String str2 = new String("abc");

        System.out.println(str1.hashCode());    //abc 문자열의 해시 코드 값 출력
        System.out.println(str2.hashCode());

        Integer i1 = new Integer(100);
        Integer i2 = new Integer(100);

        System.out.println(i1.hashCode());  //Integer(100)의 해시 코드 값 출력
        System.out.println(i2.hashCode());

    }
}

hashCode() 메서드 재정의하기

package object;

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) { //equals() 메서드 재정의
        if(obj instanceof Student){
            Student std = (Student)obj;
            if( studentId == std.studentId) //재정의한 equals() 메서드는 학생의 학번이 같으면 true 반환
                return true;
            else return false;
        }
        return false;
    }

    @Override
    public int hashCode() {     //해시 코드 값으로 학번을 반환하도록 메서드 재정의
        return studentId;
    }

}

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는 동일하지 않습니다.");

        //주소는 다르지만 equals 의 결과가 true 인 경우
        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은 동일하지 않습니다.");

        //두 학생은 논리적으로 같기 때문에 같은 해시 코드 값을 반환한다.
        System.out.println("studentLee의 hashCode :" +studentLee.hashCode());
        System.out.println("studentSang의 hashCode :" +studentSang.hashCode());

        System.out.println("studentLee의 실제 주소값 :"+ System.identityHashCode(studentLee));
        System.out.println("studentSang의 실제 주소값 :"+ System.identityHashCode(studentSang));
    }
}

 

Clone() 메서드

  • 객체를 복제해 또 다른 객체를 반환해 주는 메서드이다.
  • clone() 메서드를 사용하려면 객체를 복제해도 된다는 의미로 클래스에 Cloneable 인터페이스를 구현해야 한다.
  • Cloneable 인터페이스를 명시하지 않으면 CloneNotSupportedException이 발생한다.
package object;

class Point{    //원점을 의미하는 Point 클래스
    int x;
    int y;

    Point(int x, int y){
        this.x = x;
        this.y = y;
    }

    public String toString(){
        return "x = " + x + "," + "y = " + y;
    }

}

class Circle implements Cloneable{      //객첼르 복제해도 된다는 의미로 Cloneable 인터페이스를 함께 선언

    Point point;
    int radius;

    Circle(int x, int y, int radius){
        point = new Point(x, y);
    }

    public String toString(){
        return "원점은 " + point + "이고," + "반지름은 " + radius + "입니다";
    }


    @Override
    public Object clone() throws CloneNotSupportedException {   //clone() 메서드를 사용할 때 발생할 수 있는 오류를 예외처리 함

        return super.clone();
    }
}

public class ObjectCloneTest {

    public static void main(String[] args) throws CloneNotSupportedException {

        Circle circle = new Circle(10, 20, 30);
        Circle copyCircle = (Circle)circle.clone();     //clone() 메서드를 사용해 circle 인스턴스를 copyCircle에 복제함

        System.out.println(circle);
        System.out.println(copyCircle);

        System.out.println(System.identityHashCode(circle));
        System.out.println(System.identityHashCode(copyCircle));
    }
}

 

String 클래스

String을 선언하는 두 가지 방법

  • 문자열을 생성자의 매개변수로 하여 생성하는 방식 -> String str1 = new String("abc");
    • new 예약어를 사용하여 객체를 생성하는 경우는 문자열을 위한 메모리가 할당되고 새로운 객체가 생성됨
  • 이미 생성된 문자열 상수를 가리키는 방식 -> String str2 = "test";
    • 위 방식의 경우 str2가 기존에 만들어져 있던 문자열 상수의 메모리 주소를 가르키게 된다.
    • String str3 = "test" 코드를 작성하면 str2와 str3의 주소 값은 같다.

 

String 클래스의 final char[ ] 변수

  •  자바는 String 클래스를 제공해 char[ ] 배열을 직접 구현하지 않고도 편리하게 문자열을 사용할 수 있다.
  • String 클래스의 내용을 보면 char[ ] 배열은 final로 되어 있기 때문에 값이 변하지 않는다.
package string;

public class StringTest2 {

    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 = javaStr.concat(androidStr); //문자열 javaStr과 문자열 androidStr을 연결하여 javaStr에 대입

        System.out.println(javaStr);
        System.out.println("연결된 문자열 주소 값: " +System.identityHashCode(javaStr));
    }
}

contact() 메서드로 두 문자열을 연결해도 값이 바뀌지 않기 때문에 두 문자열이 연결된 새로운 문자열이 생성된다.

 

StringBuffer와 StringBuilder 클래스 활용하기

  • String 클래스는 한번 생성되면 변경되지 않기 때문에 문자열을 계속 연결하거나 변경하는 프로그램을 작성하면
    메모리가 많이 낭비되기 때문에 StringBuffer나 StringBuilder 클래스를 사용한다.
  • 두 클래스는 내부에 변경 가능한 char[ ]를 변수로 가지고 있기 때문에 문자열을 연결하면 기존에 사용하던
    char[ ] 배열이 확장되므로 추가 메모리를 사용하지 않는다.
  • 두 클래스의 차이는 여러 작업이 동시에 문자열을 변경하려 할 때 안전하게 변경되도록 보장해 주는가의 차이이다.
    • StringBuffer는 안전하게 변경되도록 보장, StringBuilder는 보장되지 않는다.
package string;

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); //String으로 부터 StringBuilder생성
        System.out.println("연산 전 buffer 메모리 주소:" + System.identityHashCode(buffer));//buffer 메모리 주소
        buffer.append(" and");                // 문자열 추가
        buffer.append(" android");            // 문자열 추가
        buffer.append(" programming is fun!!!"); //문자열 추가
        System.out.println("연산 후 buffer 메모리 주소:" + System.identityHashCode(buffer));//buffer 메모리 주소

        javaStr = buffer.toString(); //String 클래스로 반환
        System.out.println(javaStr);
        System.out.println("새로 만들어진 javaStr 문자열 주소 :" +System.identityHashCode(javaStr)); //새로 생성된 메모리 주소

    }
}

 

 

Wrapper 클래스

  • 자바에서는 기본 자료형처럼 사용할 수 있는 클래스를 제공하는데 이를 Wrapper 클래스 라고 한다.
  • 종류는 Boolean, Byte, Character, Short, Integer, Long, Float, Double이 있다.

예시 - Integer 클래스 사용하기

Integer 클래스의 생성자는 특정 정수를 매개변수로 받는 경우와 문자열을 받는 경우 두 가지가 있다

  • Integer(int value) { ... }  //특정 정수를 매개변수로 받는 경우
  • Integer(String s) //특정 문자열을 매개변수로 받는 경우

Integer 클래스 내부의 int 자료형 값을 가져오기 위해서는 intValue() 메서드를 사용한다

  • Integer iValue = new Integer(100);
    Int myValue = iValue.intValue( );  //int 값 가져오기, myValue 값을 출력하면 100이 출력됨

valueOf() 정적 메서드를 사용하면 생성자를 사용하지 않고 정수나 문자열을 바로 Integer 클래스로 반환받을 수 있다.

  • Integer number1 = Integer.valueOf("100");
    Integer number2 = Integer.valueOf(100);

parseInt() 메서드를 활용하면 문자열이 어떤 숫자를 나타낼 때, 문자열로 전달된 경우에 문자열에서
int 값을 바로 가져와서 반환할 수도 있다.

  • int num = Integer.parseInt("100");

 

오토박싱과 언박싱

  • 기보 자료형과 Wrapper 클래스는 같은 값을 나타내지만, 그 쓰임과 특성이 전혀 다르다.
  • Integer와 int형으로 선언한 두 값을 더한다면 Integer에서 intValue() 메서드를 사용하거나
    int형으로 선언된 변수의 값을 Integer로 만들어 연산해야 했기 때문에 나온 것이 오토박싱과 언박싱이다.
  • 오토박싱(autoboxing)은 기본형을 객체형으로, 언박싱(unboxing)은 객체형을 기본형으로 바꾸는 것이다.
    • Integer num1 = new Integer(100);
      int num2 = 200;
      int sum = num1 + num2;  //num1을 num.intValue()로 변환(언박싱)
      Integer num3 = num2;  //Integer.valueOf(num2)로 변환(오토박싱)

 

Class 클래스

  • 모르는 클래스의 정보를 사용할 경우에 클래스를 직접 찾아야하는데 이때 Class 클래스를 사용한다.
  • Class 클래스를 선언하고 클래스 정보를 가져오는 방법은 세 가지가 있다.
    1. Object 클래스의 getClass() 메서드 사용하기
      • String s = new String();
        Class c - s.getClass();  //getClass() 메서드의 반환형을 Class
      • 모든 클래스가 사용 할 수 있는 메서드이다
    2. 클래스 파일 이름을 Class 변수에 직접 대입하기
      • Class c = String.Class;
      • 컴파일된 클래스 파일이 있으면 클래스 이름만으로 Class 클래스를 반환받는다.
    3. Class.forName("클래스 이름") 메서드 사용하기
      • Class c = Class.forName("java.lang.String");
      •  컴파일된 클래스 파일이 있으면 클래스 이름만으로 Class 클래스를 반환받는다.

Person 클래스 생성

// Person 클래스 생성
package classex;

public class Person {

    private String name;
    private int age;


    public Person(){}   //디폴트 생성자

    public Person(String name){     //이름만 입력받는 생성자
        this.name = name;
    }

    public Person(String name, int age){    //이름과 나이를 입력받는 생성자
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Person 클래스 가져오기

package classex;

public class ClassTest {

    public static void main(String[] args) throws ClassNotFoundException {
        //forName() 메서드에서 발생하는 예외를 처리함, 이름과 일치하는 클래스가 없는 경우 ClassNotFoundExeption 에러 발생

        Person person = new Person();
        Class pClass1 = person.getClass();  //Object 의 getClass() 메소드 사용하기
        System.out.println(pClass1.getName());

        Class pClass2 = Person.class;    // 직접 class 파일 대입하기
        System.out.println(pClass2.getName());

        Class pClass3 = Class.forName("classex.Person"); // 클래스 이름으로 가져오기
        System.out.println(pClass3.getName());           //이름과 일치하는 클래스가 없는경우 ClassNotFoundException 발생함
    }
}

 

Class 클래스를 활용해 클래스 정보 알아보기

  • Class 클래스를 활용하여 그 클래스의 정보를 가져오고, 그 정보를 활용하여 인스턴스를 생성하거나
    메서드를 호출하는 방법을 리플렉션이라고 한다.
package classex;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class StringClassTest {

    public static void main(String[] args) throws ClassNotFoundException {

        Class strClass = Class.forName("java.lang.String"); //클래스 이름으로 가져오기

        Constructor[] cons = strClass.getConstructors();    //모든 생성자 가져오기
        for(Constructor c : cons){
            System.out.println(c);
        }
        System.out.println();
        Field[] fields = strClass.getFields();  //모든 멤벼 변수(필드) 가져오기
        for(Field f : fields){
            System.out.println(f);
        }
        System.out.println();
        Method[] methods = strClass.getMethods();   //모든 메서드 가져오기
        for(Method m : methods){
            System.out.println(m);
        }
    }
}

 

newInstance()를 사용해 클래스 생성하기

  • Class 클래스를 사용하여 얻어온 클래스 정보를 가직 인스턴스를 생성하는데 쓰인다.
  • newInstance() 메서드는 항상 Object를 반환하므로 생선된 객체형으로 형 변환해야 한다.
package classex;

public class NewInstanceTest {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        Person person1 = new Person();  //생성자로 생성하기
        System.out.println(person1);

        Class pClass = Class.forName("classex.Person");
        Person person2 = (Person)pClass.newInstance();  //Class 클래스의 newInstance() 메서드로 생성하기
        System.out.println(person2);
    }
}

 

Class.forName()을 사용해 동적 로딩하기

  • 프로그램 실행 이후 클래스의 로딩이 필요한 경우 클래스의 동적 로딩을 사용하는데 자바에서는
    Class.forName() 메서드를 동적 로딩으로 제공한다.
  • Class.forName() 메서드를 사용할 때 () 안에 클래스 이름이 문자열 값이기 때문에 문자열에 오류가 있어도
    컴파일할 때에는 오류를 알 수 없다.