[After Java Semina] 기본 클래스

Jiwon-Woo·2021년 9월 2일
0

After Java Semina

목록 보기
1/5

0. JDK에서 제공하는 패키지

자바를 설치하면 JDK(Java Development Kit : 자바 개발자 도구)가 설치된다. 개발자들의 편의를 위해 오라클이 미리 만들어놓은 클래스의 집합으로, 이 클래스들은 기능별로 구분되어 패키지 단위로 제공된다. 이렇게 자바에서 기본으로 제공하는 패키지를 자바 API라고 한다.

자바 API 문서

cd ~/Library/Java/JavaVirtualMachines/adopt-openjdk-11.0.11/Contents/Home/lib

내 맥북의 경우, 이 경로를 통해 가면 jrt-fs.jarsrc.zip 을 만날 수 있었다. jrt-fs.jar 에는 JDK의 패키지들이 압축되어 있고, src.zip 에는 소스코드들이 담겨있다.

만약 .jar 파일 압축을 풀고 싶다면, jar xvf 파일명.jar 명령어를 사용하면 된다.


1. Object 클래스

1.1 java.lang 패키지

java.lang패키지는 JAVA프로그래밍에 필수적인 클래스들을 모아놓은 패키지이다. import문을 사용해야하는 다른 패키지들과 달리, import없이 자동으로 java프로그래밍에 포함된다.

java.lang패키지는 JVM경로/Contents/Home/lib/src.zip 내부에 존재하며, 컴파일시 컴파일러가 코드에 import java.lang.*;구문을 자동으로 삽입하여 컴파일 한다.

java.lang패키지에는 아래와 같은 클래스들이 포함된다.

String, StringBuffer,Process, Runtime, Thread
Math, StrictMath,Exception Throwable, Error
Package, Class, ClassLoader, Wrapper, System, Stream

참고 : Incodom, 코딩하는 털보 블로그

1.2 Object 클래스

Object 클래스는 모든 클래스의 최상위 클래스이다. 즉 모든 클래스는 Object클래스로부터 상속을 받는다. 또한 행렬을 포함한 모든 객체는 Object 클래스의 메소드들을 구현한다(from Oracle Doc).

Object 클래스에는 다음과 같은 메소드들이 있다.

1.3 toString() 메서드

현재 객체의 정보를 문자열을 반환하는 메소드. 아래와 같이 정의되어있다.

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

1.4 equals() 메서드

매개변수로 들어온 참조변수가 같은 객체를 참조하는지 판단하는 메소드. 즉 물리적으로 동일한 객체인지 확인한다. 아래와 같이 정의되어있다.

public boolean equals(Object obj) {
        return (this == obj);
    }

필요에 의해 overriding으로 논리적으로 동일한지 확인하는 메소드로 변경할 수 있다.

public class TestClass {
        private String name;
        public TestClass(String name) {
            this.name = name;
        }
        
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof TestClass) {
                return (name == ((TestClass) obj).name);
            }
            return false;
        }
}

...

public class Main {

    public static void main(String[] args) {
        TestClass testClass = new TestClass("TTest");
        TestClass testClass2 = new TestClass("TTest");
        System.out.println(testClass.equals(testClass2));
    }
}

위의 코드의 경우 물리적으로는 다른 객체이지만, 내용물이 같으므로 논리적으로는 같은 객체이다. 이러한 경우 필요에 따라 equals 메소드를 오버라이딩하여 바꿔서 사용할 수 있다.

1.5 hashCode() 메서드

해당 객체가 참조하고 있는 주소를 10진수로 반환 하는 함수

@HotSpotIntrinsicCandidate
    public native int hashCode();

@HotSpotIntrinsicCandidate : 아래의 메소드는 HotSpot에서 최적화된 형태로 코드가 작성되었기 때문에, JDK가 구현한 소스 코드를 대체하겠다는 어노테이션이다. 참고 사이트. HotSpot은 JVM이름인데, 1.2버전부터 사용된 유서깊은 JVM이다.

native : 자바 네이티브 인터페이스. JVM위에서 실행되고 있는 자바코드가 네이티브응용프로그램(JVM을 돌리는 하드웨어/OS에 종속된 프로그램)이나, C/C++/어샘블리등 타 언어로 작성된 라이브러리를 호출하거나 호출될 수 있게 해주는 키워드.

native 추가 설명

equals()메서드는 해당 메소드가 동일한지 확인하는 메소드이다. 또한, 같은 객체라면 같은 hashcode값을 반환해야한다. 따라서 equals 오버라이딩을 하여 같은 객체의 정의를 변경한다면, hashCode()메소드도 같이 오버라이딩하여 같은 hashcode값을 반환하게 해야한다.

참고 : 망나니 개발가 블로그, nesoy 블로그

1.6 clone() 메서드

객체의 복사본을 만드는 메서드. OOP의 '정보은닉성'에 위배될 수 있다. 그러므로 아래처럼 복사 기능을 넣을 클래스의 선언부에 clonable 인터페이스를 명시하고 재정의하여 사용한다.

public class TestClass **implements Cloneable**{
        private String name;
        public TestClass(String name) {
            this.name = name;
        }

    **@Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }**
}

1.7 notify , notifyAll , wait () 메서드

이 3가지의 메서드는 Thread를 제어할 때 필요한 메서드 들이다.

wait - 갖고 있던 고유락을 해제하고 스레드를 잠들게 한다.

notify - 잠들어 있던 스레드 중 임의로 하나를 골라 깨운다.

notifyAll - 호출로 잠들어 있던 스레드를 모두 깨운다.

이 3가지의 메서드들은 호출하는 스레드가 반드시 고유 락을 갖고 있어야 한다. 다시 말해, synchronized 블록 내에서 호출되어야 한다.

wait() 메서드를 호출하면 락을 해제하고, 스레드는 잠이 든다. 누군가 깨워줄 때 까지 wait()은 리턴되지 않는다.

notify(), notifyAll() 메서드는 둘 다 wait()으로 잠든 메서드를 깨운다. 둘의 차이는 잠든 스레드 하나만 깨우냐, 모두 깨우냐의 차이이다.

notify() 메서드는 어느 스레드를 깨울지 선택할 수 없기 때문에 제어가 어렵다. 그래서 보통은 notifyAll()을 사용한다. notifyAll()이 모든 스레드를 깨우긴 하지만 이 메서드를 호출한다고 해서 잠들어 있던 모든 스레드가 동시에 동작하는 것은 아니다.

public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;

1.8 finalize() 메서드

자바의 메모리 해제는 Garbage Collector(이하 GC)에 의해 수행됩니다. finalize()는 GC에 의해 호출된다.

파이널 라이저의 주요 목적은 오브젝트가 메모리에서 제거되기 전에 오브젝트가 사용하는 자원을 해제하는 것이다.

protected void finalize() throws Throwable {}

2. String 클래스

2.1 String 클래스 선언 방식 두가지

String 객체를 선언하는 방식은 new연산자를 이용하여 생성자로 생성하는 방법과, =를 이용해 리터럴 대입으로 생성하는 방식 두가지가 있다.

byte[] bytes = {72, 101, 108, 108, 111, 32,74, 97, 118, 97};
String test = new String(bytes);
String test2 = new String(bytes, 2, 6);

System.out.println(test);
// > Hello Java

System.out.println(test2);
// > llo Ja

2.2 String 클래스의 final char[] 변수(현재는 final byte[])

String클래스가 입력받은 문자열을 저장하는 프로퍼티. 아래처럼 정의되어 있다.

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
...
@Stable
    private final byte[] value;
...
}

String 심화 : 호식이네 블로그, 진성소트프 블로그

char[] 대신 byte[]차이점 : janjanlog 벨로그

String은 어떻게 별도의 메소드 호출 없이, 객체를 호출하면 자동으로 문자열을 반환하는거지????

우선, 객체를 메소드 없이 호출하면 자동으로 toString()메소드가 컴파일러에 의해 자동으로 호출된다.

그리고 String 클래스 및 File클래스에서는 toString()메소드를 재정의하여 의도한 값을 리턴하게 만들었다.

⇒ String객체자체를 호출하는 경우, 컬파일러에서 자동으로 toString()를 호출하여 사용자가 의도한 문자열을 반환하게 한다.

참고 : xemaker 블로그

2.3 StringBuffer 클래스의 개념과 활용

StringBuffer는 String처럼 문자열을 저장할 수 있고, 문자열 추가(append) 및 변경에 String보다 유리한 클래스이다.

public class Main {

    public static void main(String[] args) {
        String str1 = new String("str1");
        StringBuffer str2 = new StringBuffer("str2");
        System.out.println("String\t\t\t: " + str1.hashCode());
        System.out.println("StringBuffer\t: " + str2.hashCode());
        System.out.println("[add string]");
        str1 += "424242";
        str2.append("424242");
        System.out.println("String\t\t\t: " + str1.hashCode());
        System.out.println("StringBuffer\t: " + str2.hashCode());
    }
}
String		: 3541024
StringBuffer	: 434091818
[add string]
String		: -1444123366
StringBuffer	: 434091818

String형인 str1의 경우 문자열이 변경되자 hashcode값이 변경되었지만(= 이전과 이후가 다른 객체이다.)
StringBuffer형인 str2의 경우 hashcode값이 그대로 유지됨을 알 수 있다.

2.4 String class 안에 내제 되어있는 method

java_api_string

2.5 String 객체 초기화

String 의 초기화는 직접 문자열 리터럴로 초기화 하든지 아니면 new 연산자로 초기화를 한다.

String 리터럴은 공통 pool에 저장이 된다. 이 영역은 저장공간을 아끼기 위해서 같은 내용의 문자열은 공유를 하게끔 만든다. 반면에 String 객체는 힙 영역에 저장이 되고 같은 내용의 문자열이라도 공유를 하지 않는다.

String s1 = "Hello";// String literal
String s2 = "Hello";// String literal
String s3 = s1;// 같은 참조
String s4 =new String("Hello");// String object (객체)
String s5 =new String("Hello");// String object (객체)

String 초기화의 차이

출처: https://pjh3749.tistory.com/255


3. Wrapper 클래스

3.1 Wrapper(포장) 클래스란

자바에서는 기본타입(byte, char, short, int, long, float, double, boolean)의 값을 가지고 있는 객체(프로퍼티랑 다름)를 생성할 수 있다. 이러한 객체를 포장클래스라고 한다. 내부의 값을 변경할 수 없으므로, 변경하고자 한다면 새로운 객체가 생성해야한다.

기존 자료형의 앞 자리를 대문자로 바꾼 형태로 선언하며, 생성자 내부에 문자열을 주어도 잘 변경해서 사용한다.

//Character objChar = new ~~Character~~('가');
//Integer objInteger1 = new ~~Integer~~(42);
//Integer objInteger2 = new ~~Integer~~("42");
//Boolean objBoolean1 = new ~~Boolean~~(false);
//Boolean objBoolean2 = new ~~Boolean~~("false");
Character objChar = '가';
Integer objInteger1 = 42;
Integer objInteger2 = Integer.valueOf("42");
Boolean objBoolean1 = Boolean.FALSE;
Boolean objBoolean2 = Boolean.FALSE;
  • 기존에는 주석내용처럼 선언을 했지만, 취소선과 같이 아래와 같은 경고문과 언박싱, valueOf()메소드, 그리고 simple형를 사용할 것을 권장하였다(권장사항은 주석 밑의 선언형태).

    Inspection info: Reports explicit boxing, that is wrapping of primitive values in objects.
    Explicit manual boxing is unnecessary as for Java 5 and later, and can safely be removed.
    Examples:
    Integer i = new Integer(1); → Integer i = Integer.valueOf(1);
    int i = Integer.valueOf(1); → int i = 1;
    ...
    Number constructor call with primitive argument
    Inspection info:
    Reports any attempt to instantiate a new Long, Integer, Short, or Byte object from a primitive long, integer, short, or byte argument.
    It is recommended that you use the static method valueOf() introduced in Java 5. By default, this method caches objects for values between -128 and 127 inclusive.
    Example:
    Integer i = new Integer(1);
    Long l = new Long(1L);
    After the quick-fix is applied, the code changes to:
    Integer i = Integer.valueOf(1);
    Long l = Long.valueOf(1L);

대충 요약하면 Java5 이후부터는 명시적인 수동 박싱(Integer 변수 = new Integer(숫자))은 불필요하다는 이야기 이며, 자주 사용되는 -128~128사이의 값들은 매번 포장객체로 만들 필요가 없다는 내용이다(자주 사용되고, 값이 변할수 있으므로 -128~128사이의 값들은 매번 객체를 만드는 대신 상수풀에서 가져다 쓴다).

굳이 포장클래스를 사용하는 이유는 무엇인가?

  • ArrayList 등과 같은 Collection 프레임 워크의 데이터 구조는 기본 타입이 아닌 객체만 저장하게 되고 Wrapper Class를 사용하여 자동 방식과 언방식이 일어 난다.
  • 기본형 값이 아닌 객체로 저장되어야 할 때가 있음 (java.util package 의 내부 클래스는 객체만 처리함)
  • 객체간의 비교가 필요할 때가 있음

3.2 Integer class method

3.2.1 INTEGER.PARSEINT(STRING S)

  • String → int. 문자열을 int형으로 변환한다. 어쩌면 Integer에서 가장 많이 쓰이는 메소드
public class IntegerClass {
    public static void main(String []args)
    {
        String str = "123";
        int num = Integer.parseInt(str);
        System.out.println("str = " + str);
        System.out.println("num = " + num);
    }
}

/*
실행 결과
str = 123
num = 123
*/

3.2.2 INTEGER.TOSTRING(INT I)

  • int to String. 반대로 int를 String으로 변환한다.
public class IntegerClass2 {
    public static void main(String []args)
    {
        int num1 = 1234;
        String str = Integer.toString(num1);
        System.out.println("str = " + str);
        System.out.println("num1 = " + num1);
    }
}

/*
실행 결과
str = 1234
num1 = 1234
*/

3.2.3 INTEGER.TOBINARAYSTRING(INT I)

  • 10진수를 2진수로 변환해 String으로 리턴한다. int를 String으로 변환해 toCharArray()나 charAt()으로 찢고, 하나씩 빼는 방법도 있지만, 그런 과정을 생략할 수 있어 편리하다. 다만 음수는 제대로 작동하지 않는 것 같아 보인다.
public class IntegerClass3 {
    public static void main(String []args)
    {
        int num = 1234;
        String str = Integer.toBinaryString(num);
        System.out.println("2진수 = " + str);
    }
}

/*
실행 결과
2진수 = 10011010010
*/

3.2.4 INTEGER.TOOCTALSTRING(INT I)

  • 10진수->8진수.

3.2.5 INTEGER.TOHEXSTRING(INT I)

  • 10진수->16진수

3.2.6 INTEGER.PARSEINT(STRING S, INT RADIX)

  • 2진수→10진수

3.2.7 INTEGER.MAX(INT A, INT B)

  • 두 수를 비교하여 큰 수를 출력
public class IntegerClass4 {
    public static void main(String []args) {
        int num1 = 5;
        int num2 = 3;
        System.out.println(Integer.max(num1, num2));
    }
}
실행 결과
5

3.3 오토박싱과 언박싱

3.3.1 박싱(Boxing)과 언박싱(UnBoxing)

래퍼 클래스(Wrapper class)는 산술 연산을 위해 정의된 클래스가 아니므로, 인스턴스에 저장된 값을 변경할 수 없습니다.

단지, 값을 참조하기 위해 새로운 인스턴스를 생성하고, 생성된 인스턴스의 값만을 참조할 수 있습니다.

위의 그림과 같이 기본 타입의 데이터를 래퍼 클래스의 인스턴스로 변환하는 과정을 박싱(Boxing)이라고 합니다.

반면 래퍼 클래스의 인스턴스에 저장된 값을 다시 기본 타입의 데이터로 꺼내는 과정을 언박싱(UnBoxing)이라고 합니다.

public class Wrapper {
    public static void main(String []args)
    {
        Integer num1 = new Integer(10); //Boxing
	      Integer num2 = new Integer(13); //Boxing
        int int1 = num1.intValue();   //UnBoxing
        int int2 = num2.intValue();   //UnBoxing
        Integer result1 = num1 + num2;
        Integer result2 = num2 - num1;
        int result3 = num1 * num2;
        System.out.println(result1);
        System.out.println(result2);
        System.out.println(result3);
    }
}

/*
실행 결과
23
3
130
*/

3.3.2 오토 박싱(Auto Boxing)과 오토 언박싱(Auto UnBoxing)

JDK 1.5부터는 박싱과 언박싱이 필요한 상황에서 자바 컴파일러가 이를 자동으로 처리해 줍니다.

이렇게 자동화된 박싱과 언박싱을 오토 박싱(AutoBoxing)과 오토 언박싱(AutoUnBoxing)이라고 부릅니다.

public class Wrapper2 {
    public static void main(String []args)
    {
        Integer num1 = new Integer(10);
        int int1 = num1.intValue();
        System.out.println("Boxing :" + num1);
        System.out.println("UnBoxing :" + int1);
        Character char1 = new Character('X');
        char ch1 = char1.charValue();
        System.out.println("Boxing : " + char1);
        System.out.println("UnBoxing :" +ch1);
        System.out.println("====================Auto======");
        Integer num2 = 12; //auto boxing;
	      int int2 = num2;  //auto unboxing;
        System.out.println("Boxing :" + num2);
        System.out.println("UnBoxing :" + int2);
    }
}

/*
실행 결과
Boxing :10
UnBoxing :10
Boxing : X
UnBoxing :X
====================Auto======
Boxing :12
UnBoxing :12
*/

4. Class 클래스

4.1 Class 클래스란

Java에서 사용되는 Class들에 대한 정의를 하고 있는 틀이 Class.class 입니다.

Instances of the class Class represent classes and interfaces in a running Java application. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions.

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

알 수 있는 정보들 : 경로를 포함한 클래스명, 클래스의 생성자(클래스명), 필드, 메소드정보 등등...

얻는 방법

  1. 클래스로부터 얻는 방법

    1. Class clazz = 클래스명.class
    2. Class clazz = Class.forName("패키지...클래스명")
  2. 컴파일된 인스턴스로부터 얻는 방법

    Class clazz = 인스턴스.getClass()

  • 예제코드
    package classClass.example;

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

    public class classClassTest {

        public static void main(String[] args) throws Exception {
            **Class clazz1 = Member.class;
            Class clazz2 = Class.forName("classClass.example.Member");**
            Member member = new Member();
            **Class Clazz3 = member.getClass();**
            System.out.println("clazz1.getClass()");
            System.out.println(">> " + clazz1.getClass());
            System.out.println("clazz1.getName()");
            System.out.println(">> " + clazz1.getName());
            System.out.println("\nclazz2.getClass()");
            System.out.println(">> " + clazz1.getClass());
            System.out.println("clazz2.getName()");
            System.out.println(">> " + clazz1.getName());
            System.out.println("\nclazz3.getClass()");
            System.out.println(">> " + clazz1.getClass());
            System.out.println("clazz3.getName()");
            System.out.println(">> " + clazz1.getName());
            System.out.println("--[field]--");
            **Field[] fields = clazz1.getDeclaredFields();**
            for (Field temp : fields)
                System.out.println(temp.getName());
            System.out.println("--[method]--");
            **Method[] methods = clazz1.getDeclaredMethods();**
            for (Method temp : methods)
                System.out.println(temp.getName());
        }
    }
    clazz1.getClass()
    >> class java.lang.Class
    clazz1.getName()
    >> classClass.example.Member

    clazz2.getClass()
    >> class java.lang.Class
    clazz2.getName()
    >> classClass.example.Member

    clazz3.getClass()
    >> class java.lang.Class
    clazz3.getName()
    >> classClass.example.Member
    --[field]--
    id
    age
    --[method]--
    setId
    getAge
    setAge
    getId

리플렉션 : 객체를 통해 클래스의 정보를 분석해나가는 프로그래밍 기법. 리플렉션을 통해 동적으로 객체를 생성하는 것 또한 가능해진다(임의로 전달받은 객체의 정보를 받아서, 동적으로 객체 생성). 스프링 프레임워크에서 아주 많이 사용한다고 한다.
참고 : https://joont.tistory.com/165

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

동적으로 객체를 생성(인스턴스화)할때 사용하는 메소드.

  • new를 이용해 개발자가 객체정보를 파악하고 타이핑하여 생성하는게 정적이라고 한다면,
  • newInstance()를 이용하면 임의의 모르는 객체를 로직을 이용해 동적으로 생성할 수 있다.
Class memberClass = Class.forName("newinstance.example.Member"); //동적 로딩
Member member = (Member) memberClass.~~newInstance~~();

memberClass.newInstance();'newInstance()' is deprecated. newInstance()와 생성자를 이용해 생성하는 방식으로 변경됨.

package newinstance.example;

import java.lang.reflect.*;
import java.security.cert.Extension;

public class NewInstanceTest {
    public static void main(String[] args) throws Exception {
        Class[] parameterTypes = new Class[]{int.class, int.class}; //...(1)
        Class memberClass = Class.forName("newinstance.example.Member"); //...(2)
        Constructor memberConstructor = memberClass.getConstructor(parameterTypes); //...(3)
        Member member = (Member) memberConstructor.newInstance(1010, 24); //...(4)
        System.out.println(member.getId() + " : " + member.getAge());
    }
}

(1) int.class( = Integer.TYPE). int.class에 대한 정보는 찾기 힘들기 때문에, Oracle docs에서 Integer.TYPE필드에 대한 정보를 찾아보면 The Class instance representing the primitive type int.라고 나온다. 즉, 기본 타입인 int형을 나타내는 Class클래스의 인스턴스라는 뜻이다.

사용하고자하는 Member 클래스의 생성자의 파라매터가 (int id, int age)이기 때문에, parameterTypes에 배열형태로 int.class를 저장한다.

(2) Class 클래스인 memberClass에 Class.forName() 메소드를 이용해 Member클래스정보를 저장한다.

(3) getConstructor(Class<?>... parameterTypes)는 parameterTypes과 동일한 생성자 객체(생성자에 대한 정보 및 엑세스)를 반환. 매개변수가 맞지 않으면 IllegalArgumentException를 throw한다.

(4) Constructor클래스의 newInstance(Object... initargs) 메소드는 저장 및 엑세스 되어있는 생성자에 매개변수(initargs)를 이용하여 새 인스턴스를 만들고 초기화하여 반환한다.

Oracle docs : Class class, Constructor, Intger

참고 : https://seeminglyjs.tistory.com/246, https://joont.tistory.com/167, https://kephilab.tistory.com/97

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

동적로딩이란 Class.forName(String className)메소드를 통해, 런타임에 결정된 className으로 클래스 정보를 얻고, 객체를 생성하는 것이다. 사실 위에서 한 것에서 조금만 변형시키면 된다.

참고 : https://jin-bpro.tistory.com/entry/동적-로딩-ClassforName

0개의 댓글