[그린컴퓨터] Server/JAVA(자바 JDK)

Class 클래스 { Class 클래스란, Class 클래스를 활용해 클래스 정보 알아보기, newInstance( ) 사용하여 클래스 생성, Class.forName( ) 을 사용해 동적 로딩하기 }

Ben의 프로그램 2023. 6. 7. 13:44
728x90

Class 클래스란?

  • 자바의 모든 클래스와 인터페이스는 컴파일되고 나면 class 파일로 생성됩니다. 
  • 예를 들어 a.java 파일이 컴파일되면 a.class 파일이 생성되고 이 class 파일에는 클래스나 인터페이스에 대한 변수, 메서드, 생성자 등의 정보가 들어 있습니다. 
  • Class 클래스는 컴파일 된 class 파일에 저장된 클래스나 인터페이스 정보를 가져오는 데 사용합니다. 
  • 지금까지 변수를 선언할 때 자료형을 미리 파악하고 그 자료형에 따라 변수를 선언했습니다. 
  • 그리고 클래스를 사용할 때도 이미 그 클래스 정보(변수, 메서드 등)를 알고 있는 상황에서 프로그램을 만들었습니다.
  • 그런데 어떤 경우에는 여러 클래스 중에 상황에 따라 다른 클래스를 사용해야 할 때도 있고, 반환받는 클래스가 정확히 어떤 자료형인지 모를 때도 있습니다. 
  • 이렇게 모르는 클래스의 정보를 사용할 경우에 우리가 클래스 정보를 직접 찾아야 합니다. 
  • 이때 Class 클래스를 활용합니다. 
  • Class 클래스를 선언하고 클래스 정보를 가져오는 방법은 다음과 같이 세 가지가 있습니다. 

1. Object 클래스의 getClass( ) 메서드 사용하기

String s = new String( );
Class c = s.getClass( );  // getClass( ) 메서드의 반환형은 Class 입니다.
  • 1 번의 경우 Object 에서 선언한 getClass( ) 메서드이기 때문에 모든 클래스가 사용할 수 있습니다.
  • 이 메서드를 사용하려면 이미 생성된 인스턴스가 있어야 합니다. 

2. 클래스 파일 이름을 Class 변수에 직접 대입하기

Class c = String.Class;

3. Class.forName("클래스 이름") 메서드 사용하기 

Class c = Class.forName("java.lang.String");
  • 2번과 3번의 경우에는 컴파일된 클래스 파일이 있다면 클래스 이름만으로 Class 클래스를 반환받습니다.

예제 보면서 Class 클래스 반환 공부해보기

  • 다음 예제를 보면서 Class 클래스를 반환받고 활용해 보겠습니다. 
  • 우선 테스트에 사용할 Person 클래스를 하나 생성하겠습니다. 
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 클래스는 생성자가 3개이고, 각 멤버 변수에 get( ) 메서드와 set( ) 메서드를 제공합니다. 
  • 이를 컴파일하여 Person.class 파일을 생성합니다. 
  • 이제 Person 의 Class 클래스를 가져오겠습니다. 
public class ClassTest {

	public static void main(String[] args) throws ClassNotFoundException { // forName( ) 메서드에서 발생하는 예외를 처리함. 이름과 일치하는 클래스가 없는 경우 ClassNotFoundException 발생
		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());
	}
}

출력값

  • 여기에서 신기하게 보이는 것이 하나 있습니다. 
  • 바로 forName( ) 메서드를 사용하는 부분입니다. 
  • forName( ) 메서드를 살펴보면, 클래스 이름으로 가져오는 경우에 매개변수로 쓰이는 값이 문자열입니다. 
  • 이때 매개변수로 받은 문자열에 해당하는 클래스가 존재하지 않으면 클래스를 가져오는 데 실패합니다. 
  • 이때 ClassNotFoundException 이 발생합니다. 
  • 아무튼, 출력값을 보면 모두 classes.Person 으로 잘 출력되는 것을 볼 수 있습니다. 

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

  • 프로그래밍을 하다 보면 내가 사용할 클래스의 자료형을 모르는 경우가 있을 수 있습니다. 
  • 예를 들면 내 컴퓨터에 저장되어 있지 않은 객체를 메모리에 로드하고 생성하는 경우 그 객체의 정보를 알 수 없겠죠. 
  • 이때 Class 클래스를 가져올 수 있다면 해당 클래스 정보, 즉 생성자&메서드&멤버 변수 정보를 찾을 수 있습니다.
  • 이렇게 사용하려는 클래스의 자료형을 모르는 상태에서 Class  클래스를 활용하여 그 클래스의 정보를 가져오고, 이 정보를 활용하여 인스턴스를 생성하거나 메서드를 호출하는 방식을 '리플렉션 reflection'이라고 합니다. 
  • 사실 우리가 자바로 프로그램을 구현할 때 리플렉션 프로그래밍을 해야 하는 경우는 많지 않기 때문에, 이런 방법이 있다는 것 정도만 알아 두면 됩니다. 
  • 그럼 예제를 통해 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);
		}
	}
}
  • 우리는 지금  String 클래스의 정보가 궁금합니다. 
  • 그런데 현재 우리 프로젝트 파일 안에는 해당 String 클래스 정보가 없는 것이죠. 
  • 이럴 때 Class 클래스를 활용합니다. 
  • 우선 String 의 Class 클래스를 가져오기 위해서 Class.forName 을 활용하여 클래스 이름으로 가져옵니다. 
  • 그 다음 String 클래스의 생성자를 담기 위한 생성자 배열, 필드를 담기 위한 필드 배열, 메서드를 담기 위한 메서드 배열을 만들고 String 클래스에서 get~ 메서드를 활용하여 그릇에 담습니다. 
  • 출력 결과를 보면 다음과 같습니다. 

  • 출력 값을 보면 String 클래스의 생성자, 필드, 메서드를 볼 수 있습니다.
  • Class 클래스의 또 다른 기능으로는 여기서는 다루지 않았지만 생성자나 메서드를 직접 호출할 수도 있습니다. 

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

  • 지금까지는 Class 클래스를 사용하여 클래스 정보만을 확인해 보았습니다.
  • 그러면 이 정보를 바탕으로 인스턴스도 생성할 수 있을까요? 
  • Class 클래스의 메서드 중 newInstance( ) 메서드를 사용하면 인스턴스를 생성할 수 있습니다. 
  • newInstance( ) 메서드는 항상 Object 를 반환하므로 생성된 객체형으로 형 변환해야 합니다. 
  • 앞에서 만든 Person 클래스의 인스턴스를 Class 클래스와 newInstance( ) 메서드를 사용하여 생성해 보겠습니다. 
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"); // 이름으로 Class 가져오기
		Person person2 = (Person)pClass.newInstance();   // Class 클래스의 newInstance( ) 메서드로 생성하기
		System.out.println(person2);
	}

}

출력값

  • 위 코드를 살펴보겠습니다.
  • 위 코드에서는 두 가지 방식으로 인스턴스를 생성하고 있습니다. 
  • 첫 번째는 기존에 우리가 하던 방식으로 Person 클래스의 변수를 선언하고 생성자를 사용하여 생성하는 방법입니다. 
  • 두 번째는 이 장에서 학습한 Class 클래스의 newInstance( ) 를 사용하여 인스턴스를 생성해 보았습니다.  
  •  newInstance 의 반환 반환 값이 Object 이므로 Person 클래스로 다운 캐스팅한 것을 알 수 있습니다. 

Class 클래스를 사용하는 리플렉션은 언제 써야 하는가?

  • Class 클래스를 사용하는 방법은 클래스의 자료형을 직접 사용하여 프로그래밍하는 것보다 더 복잡하고, 예외 처리도 해야 합니다. 
  • 이미 우리가 자료형을 알고 있는 클래스인 경우 또는 컴파일할 때 직접 참조할 수 있는 클래스는 Class 클래스를 활용할 필요가 없습니다. 
  • 클래스의 정보를 모두 알고 있는 상황에서 리플렉션 프로그래밍을 하면 오히려 코드가 복잡해지고 속도도 느려집니다.
  • 따라서 리플렉션 프로그래밍은 컴파일 시점에 알 수 없는 클래스, 즉 프로그램 실행 중에 클래스를 메모리에 로딩하거나 객체가 다른 곳에 위치해서 원격으로 로딩하고 생성할 때 사용합니다. 

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

  • 대부분의 클래스 정보는 프로그램이 로딩될 때 이미 메모리에 있습니다. 
  • 그런데 이런 경우가 있습니다.
  • 어떤 회사에서 개발한 시스템이 있는데, 그 시스템은 여러 종류의 데이터베이스를 지원합니다.
  • 오라클, MySQUL, MS-SQL 등등 여러 데이터베이스를 연동할 수 있습니다. 
  • 그런데 그렇다고 이 시스템을 컴파일할 때 모든 데이터베이스 라이브러리(드라이버)를 같이 컴파일할 필요는 없습니다. 
  • 시스템을 구동할 때 어떤 데이터베이스와 연결할지만 결정된다면 해당 드라이버만 로딩하면 됩니다. 
  • 회사가 사용하는 데이터베이스 정보는 환경 파일에서 읽어 올 수도 있고 다른 변수 값으로 받을 수도 있습니다. 
  • 즉 프로그램 실행 이후 클래스의 로딩이 필요한 경우 클래스의 '동적 로딩 dynamic loading' 방식을 사용합니다. 
  • 자바는 Class.forName( ) 메서드를 동적 로딩으로 제공합니다. 
Class pClass = Class.forName("classex.Person");
  • forName( ) 메서드를 살펴보면 매개변수로 문자열을 입력받습니다. 
  • 이때 입력받는 문자열을 변수로 선언하여 변수 값만 바꾸면 다른 클래스를 로딩할 수 있습니다. 
String className = "classex.Person"
Class pClass = Class.forNmae(className);
  • 이렇게 문자열 변수로 저장한다면 간단하게 필요한 드라이버를 로딩할 수 있습니다. 

forName( ) 메서드를 사용할 때 유의할 점

  • forName( ) 메서드를 사용하여 Class 클래스를 가져올 때 가장 유의해야 할 점은 해당 forName("클래스 이름")의 클래스 이름이 문자열 값이므로, 문자열에 오류가 있어도 컴파일할 때에는 그 오류를 알 수 없다는 것입니다. 
  • 결국 프로그램이 실행되고 메서드가 호출될 때 클래스 이름에 해당하는 클래스가 없다면 ClassNotFoundException 이 발생합니다. 
  • 따라서 동적 로딩 방식은 컴파일할 때 오류를 알 수 없습니다.
  • 하지만 앞에서 설명한 것처럼 여러 클래스 중 하나를 선택한다거나, 시스템 연동 중 매개변수로 넘어온 값에 해당하는 클래스가 로딩되고 실행되는 경우에는 동적 로딩 방식을 유연하게 사용할 수 있습니다. 
  • 동적 로딩을 통해 Class 클래스를 가져올 수 있다면 리플렉션 프로그래밍으로 객체를 생성하고 활용할 수 있습니다. 
  • 동적 로딩 방식은 자바에서만 제공하는 방식이 아닙니다.
  • 다른 언어도 실행 중에 라이브러리를 로딩하는 방식을 제공하고 있습니다. 
  • 프로그래밍 언어는 각 언어마다 특성이 있지만 공통된 부분이 더 많습니다. 
  • 따라서 하나의 언어를 잘하게 되면 다른 언어도 잘 할 수 있습니다.