토비의스프링 1장에 JDBC부분이 언급되었는데
Class.forName()메서드를 사용하는거 보고 호기심에 찾아보게되었다.
Class클래스의 인스턴스들은 실행중인 자바 애플리케이션의 클래스와 인터페이스를 나타낸다.
enum타입은 클래스의 한종류이고 annotation타입은 인터페이스의 한종류이다.
같은엘리먼트유형, 같은 차원의 배열들도 Class객체로 반영되는(reflected) 클래스에 속한다.
public 생성자가 존재하지않지만 Class객체는 클래스로더가 defineClass 메소드중 하나를 호출하여 클래스파일의 바이트를 전달할때 JVM에 의해 Class객체가 자동으로 생성된다.
Class클래스의 메소드는 클래스나 인터페이스의 많은 특징을 노출한다. 대부분의 특징들은 클래스로더가 JVM에 전달한 클래스파일에서 파생된다. 몇몇특성은 런타임시에 클래스로딩환경에 의해 결정된다.
Class클래스의 일부메소드는 자바소스코드의 클래스,인터페이스선언이 다른선언내에 포함되었는지아닌지를 노출한다.
Class에 접근하는 방법
Class<UserDao> userDaoClass = UserDao.class; // Class타입의 인스턴스를 가져오는방법 (타입을통해 가지고오는방법)
UserDao userDao = new UserDao();
Class<? extends UserDao> aClass = userDao.getClass(); // Class타입의 인스턴스를 가져오는방법(인스턴스를통해 가지고오는방법)
}
주어진 문자열이름을 가진 클래스나 인터페이스와 관련된 Class객체를 반환한다.
Class t = Class.forName("java.lang.Thread")
System.out.print("t값은뭘까" + t);
// Class a = Class.forName("void"); 에러
// System.out.println("a값은 뭘까=" + a); 에러
//
// Class b = Class.forName("int"); 에러
// System.out.println("b값은 뭘까=" + b); 에러
Class c = Class.forName("tobi.user.dao.UserDao"); // 클래스의 풀패키지네임
System.out.println("c값은 뭘까= " + c);
Class d = Class.forName("tobi.user.dao.InterfaceTypeTest"); // 인터페이스의 풀패키지네임
System.out.println("d값은 뭘까= " + d);
클래스나 인터페이스에대한 완전한이름이 주어지면 forName메소드는 클래스나 인터페이스를 찾고, 로드하고, 연결하려고 시도한다.
만약 primitive타입이나 void를 입력한경우 이름없는 패키지에서 사용자가 정의한 클래스를 찾으려고 시도한다.
즉, primitive타입이나 void를 나타내는 Class 객체를 얻는데는 사용할수 없다.
public class Book {
public static String A = "A";
private String B = "B";
public Book() {
}
public Book(String b) {
B = b;
}
public void c() {
System.out.println("C");
}
public int sum(int left, int right) {
return left + right;
}
}
public class Main{
Class<Book> bookClass1 = Book.class; // 클래스에 접근가능할경우의 리플렉션
Class<?> bookClass2 = Class.forName("tobi.user.dao.Book"); // 접근불가능할경우의 리플렉션
Constructor<?> constructor = bookClass2.getConstructor(null);
Book book = (Book) constructor.newInstance();// 새로운 인스턴스를 만듬
System.out.println(book);
Field a = Book.class.getDeclaredField("A");
System.out.println(a.get(null));
a.set(null, "AAAAAA");
System.out.println(a.get(null));
System.out.println("-------");
Field b = Book.class.getDeclaredField("B");
b.setAccessible(true); // private필드를 가져올수있게끔 해주는메서드
System.out.println(b.get(book)); // book에있는 b라는 필드값을 가져옴
b.set(book, "BBBBB");
System.out.println(b.get(book));
System.out.println("-------");
Method c = Book.class.getDeclaredMethod("sum",int.class, int.class);
c.setAccessible(true); // 접근 제어자 무시
int invoke = (int) c.invoke(book, 1, 2);
System.out.println(invoke);
}
public class Book {
private String a = "a";
private static String B = "BOOK";
private static final String C = "BOOK";
public String d = "d";
protected String e = "e";
public Book() {
}
public Book(String a, String d, String e) {
this.a = a;
this.d = d;
this.e = e;
}
private void f() {
System.out.println("F");
}
public void g() {
System.out.println("g");
}
public int h() {
return 100;
}
}
클래스를 하나 예시로 선언했다.
public class main{
Arrays.stream(bookClass.getFields()).forEach(System.out::println);// Book의 public한 필드값들을 전부 가져온다.
System.out.println("---------");
Arrays.stream(bookClass.getDeclaredFields()).forEach(System.out::println); // Book의 모든필드값들을 전부 가져온다.
System.out.println("---------");
Arrays.stream(bookClass.getMethods()).forEach(System.out::println); // Book의 모든메서드들을 전부 가져온다. 직접정의하지않고 Object에서 상속받은 메서드도 출력된다
System.out.println("---------");
Arrays.stream(bookClass.getConstructors()).forEach(System.out::println); // Book의 모든생성자들을 전부 가져온다.
}
실행결과는 위와 같다.
public한 필드값들만 반환해준다
모든 필드값들을 반환해준다.
정의한 메서드, 상속받은 메서드를 반환한다
생성자를 반환한다.
추후, Class클래스의 다른메서드를 사용하는날이 온다면 계속 추가할예정이다.
references : https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html