/* 
written by kaspy (kaspyx@gmail.com)
*/ 



자바 리플렉션(Reflection)이란 컴파일된 자바 코드에서 역으로 클래스를 불러서 메소드(Method) 및 변수(Field)를 구해오는 방법으로 클래스를 동적 로딩하여 사용할때 많이 사용되고 디컴파일할때에도 자주 사용되는 기법이다.


1. 리플렉션(Reflection) 기본


예를들어 아래와 같이 Class가 있다고 하면 생성자(Constructor)도 있을것이고, 필드(Field) 및 메쏘드(Method)도 있을것이다.


vi MyClass.java


  1. public class MyClass{
  2.  
  3.   private static String myName = null;
  4.   public String pubString = null;
  5.  
  6.   private void hiddenMethod()
  7.   {
  8.     System.out.print("hello private");
  9.   }
  10.  
  11.   public MyClass()
  12.   {
  13.   }
  14.  
  15.   public int mySum(int a,int b)
  16.   {
  17.     return a+b;
  18.   }
  19.  
  20.   public MyClass (String myname){
  21.     this.myName = myname;
  22.   }
  23.   public String getMyName()
  24.   {
  25.     return myName;
  26.   }
  27.  
  28.   public void setMyName(String myName)
  29.   {
  30.     this.myName = myName;
  31.   }
  32. } // javac MyClass.java


그렇다면 이를 사용하는 소스코드에서는 아래와같은 형식으로 사용된다.


vi MyJava.java


  1. import java.io.*;
  2.  
  3. public class MyJava
  4. {
  5.   public static void main(String args[])
  6.   {
  7.     MyClass myClass = new MyClass("kaspyx");
  8.  
  9.     String myName = myClass.getMyName();
  10.  
  11.     System.out.println("My name is "+myName);
  12.  
  13.   }
  14. } // javac MyJava.java

다시한번 설명을 하자면


MyClass myClass = new MyClass("kaspyx")

String myName = myClass.getMyName();


이런식으로 사용되는데, 이를 Reflection 으로 풀어보면 아래처럼 된다.

보통 Java 문법은 S(주어:object) V(동사:method) O(목적어:argument) 순서이지만, reflection 은 V.invoke(S,O) 순이라고 보면된다. 
먼저 클래스(Class)를 불러오고, 생성자(Constructor)를 구하고, 생성자에 인자를 넘겨 생성자를 만들고 메소드를 얻어와서 invoke() 함수로 실행하는 과정을 거친다. 이를 Reflection 으로 풀어보면 아래처럼 된다.

Class myClass = Class.forName("MyClass");
Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
Object myObj = myConstuctor.newInstance("kaspyx");
Method method = myClass.getMethod("getMyName");
String myName = (String)method.invoke(myObj);

코딩으로 확인해보자.

vi MyReflector.java


  1. import java.io.*;
  2. import java.lang.reflect.*;
  3.  
  4. public class MyReflector
  5. {
  6.   public static void main(String args[])
  7.   {
  8.     try{
  9.       Class myClass = Class.forName("MyClass");
  10.       Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
  11.       Object myObj = myConstuctor.newInstance("kaspyx");
  12.       Method method = myClass.getMethod("getMyName");
  13.       String myName = (String)method.invoke(myObj);
  14.  
  15.       System.out.println("Myname is "+ myName);
  16.     }
  17.     catch (Exception ex)
  18.     {
  19.       ex.printStackTrace();
  20.     }
  21.   }
  22. } // javac MyReflector.java

* 실행 결과화면


Reflection은 클래스의 생성자(Constructor)나 메소드에서 받는 인자 타입, 필드(Public 이냐 Private)냐 등등에따라서 불리워지는 타입에 맞춰줘야한다.  그래서 조금 헛갈린면이 없지않다

2. 메소드(Method) 및 인자값 전달하기

메소드가 위의 getMyName 처럼 인자가 String 하나면 위와같이 해줘야지만, 여러개의 String을 받는 경우라면 거기에맞게 짜줘야한다.
예를들어 setMyName(String myName) 형태라면 아래와같이해줘야한다.

Class[] methodParamClass = new Class[] {String.class};
Object[] methodParamObject = new Object[] {"Kyungsu kim"};
Method myMethod2 = getClass1.getMethod("getMyName2", methodParamClass);
myMethod2.invoke(myObj, methodParamObject);

mySum(int a, int b) 형태일 경우도 마찬가지이다. 만약  setMyName이에서 받는 문자열 인자가 여러개라면 컴마(,)로 나눠서 넘겨주면된다.

Class[] intParamClass = new Class[] {int.classint.class};
Object[] intParamObject = new Object[] {12};
Method method3 = myClass.getMethod("mySum",intParamClass);
int sum = (int)method3.invoke(myObj,intParamObject);

3. 필드(Field) 다루기

함수 호출 외에도 변수(필드)를 다룰때도 조금 다른데, setget을 이용한다.(여기서는 private 으로 선언된 myName 필드를 가지고 다루어볼것이다.)
priavte 필드(변수)에 접근하려 할때는 setAccessible(True)가 필요하며, getDeclaredField가 사용된다 아래를 참고해보기바란다.

Field field = myClass.getDeclaredField("myName");
field.setAccessible(true); // private일경우 secAccessible(true)로 해줘야 접근가능하다.
field.set(null,"No kaspy~!");
String myName2 = (String)field.get(myObj);

그럼 위에서 설명한 내용들(2,3)을 코딩에 반영해서 확인해보자.

vi MyReflector.java


  1. import java.io.*;
  2. import java.lang.reflect.*;
  3.  
  4. public class MyReflector
  5. {
  6.   public static void main(String args[])
  7.   {
  8.     try{
  9.       Class myClass = Class.forName("MyClass");
  10.       Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
  11.       Object myObj = myConstuctor.newInstance("kaspyx");
  12.       Method method = myClass.getMethod("getMyName");
  13.       String myName = (String)method.invoke(myObj);
  14.  
  15.       System.out.println("Myname is "+ myName);
  16.  
  17.       Class[] methodParamClass = new Class[] { String.class };
  18.       Object[] methodParamObject = new Object[] {"Kyungsu Kim"};
  19.       Method method2 = myClass.getMethod("setMyName",methodParamClass);
  20.       method2.invoke(myObj, methodParamObject);
  21.       myName = (String)method.invoke(myObj);
  22.  
  23.       System.out.println("Changed myname is "+ myName);
  24.  
  25.       Class[] intParamClass = new Class[] {int.classint.class};
  26.       Object[] intParamObject = new Object[] {12};
  27.       Method method3 = myClass.getMethod("mySum",intParamClass);
  28.       int sum = (int)method3.invoke(myObj,intParamObject);
  29.  
  30.       System.out.println("Sum is " + sum);
  31.  
  32.       Field field = myClass.getDeclaredField("myName");
  33.       field.setAccessible(true);
  34.       field.set(null,"No kaspy~!");
  35.       String myName2 = (String)field.get(myObj);
  36.       System.out.println("Myname2 is "+ myName2);
  37.  
  38.     }
  39.     catch (Exception ex)
  40.     {
  41.       ex.printStackTrace();
  42.     }
  43.   }
  44. } // javac MyReflector.java

* 실행 결과화면


4. 그외 리플렉션(Reflection) 함수들 


Java 에서는 위에서 설명한 함수들을 제공해주고있다. 


getMethods() , getFields() 등등을 사용하면 함수이나 변수이름을 몰라도 불러올수있으며, 이함수는 public으로 선언한 메소드(함수)나 필드(변수)를 가져온다. getMethod()는 문자열을 가지고 단일 함수를 찾아주지만 getDeclaredMethods()는 private 포함 클래스 내부에서 선언한 함수만 찾아준다.

비슷한 기능이지만 조금씩 다르니 레퍼런스를 잘 참고해서 용도에 맞게 써줘야할것이다. 


코딩을 통해 확인해보자.


vi MyReflector2.java


  1. import java.io.*;
  2. import java.lang.reflect.*;
  3.  
  4. public class MyReflector2
  5. {
  6.   public static void main(String args[])
  7.   {
  8.     try{
  9.  
  10.       Class myClass = Class.forName("MyClass");
  11.       Method methods[] = myClass.getDeclaredMethods();
  12.  
  13.       System.out.println("1.DeclaredMethods() ================");
  14.       for (Method method: methods)
  15.       {
  16.         System.out.println("Found method name: " + method);
  17.       }
  18.  
  19.       Method methods2[] = myClass.getMethods();
  20.       System.out.println("2.getMethods() =====================");
  21.  
  22.       for (Method method: methods2)
  23.       {
  24.         System.out.println("Found method name: " + method);
  25.       }
  26.  
  27.       Field fields[] = myClass.getFields();
  28.       System.out.println("3.getFields=========================");
  29.  
  30.       for (Field field: fields)
  31.       {
  32.         System.out.println("Found field name: " + field);
  33.       }
  34.  
  35.       Constructor myConstuctor = myClass.getConstructor( new Class[] {String.class });
  36.       Method hiddenMethod = myClass.getDeclaredMethod("hiddenMethod");
  37.  
  38.       System.out.println("5.=================================");
  39.       System.out.println("Hidden method name is "+hiddenMethod.getName());
  40.     }
  41.     catch (Exception ex)
  42.     {
  43.       ex.printStackTrace();
  44.     }
  45.   }
  46. } // javac MyReflector2.java


* 실행 결과화면




5. 참고자료


https://docs.oracle.com/javase/tutorial/reflect/class/classMembers.html





저작자 표시
신고

'IT > Java' 카테고리의 다른 글

자바 리플렉션(Java Reflection)  (0) 2017.01.06
자바(Java)로 구현한 RSA 암호학 알고리즘 예제  (0) 2015.11.12
Java로 달력(Calendar) 구현하기  (0) 2015.05.18
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 

RSA는 대단히 수학적인 알고리즘으로 200자리가 넘어가면 슈퍼컴퓨터로 푸는데만해도 1만년이상이 걸린다고 한다.


알고리즘은 두개의 소수 p,q 그리고 (p-1)*(q-1)의 서로소 d를 구하고


p,q,d는 비밀키로 개인이 보관(유출금지)한다.


그리고 p*q 의값 n과 e * d % (p-1)(q-1) = 1을 만족하는 e를 구한다.


n과 e는 공개키로 외부에게 공개할수있다.


p = 11, q = 17, d = 23

e = 7, n = 187


메시지가 m이라고 할때 


암호화 c = ( m ^ e  ) % n


복호화 d = ( c ^ d) % n


로 구할수있다. 큰 숫자를 다뤄야하는 특성상, 자바 자료형 BigInteger를 사용하여 구현하였다.


- 컴파일

javac RSAtest.java

- 실행

java RSAtest


  1. import java.math.BigInteger;
  2. import java.security.SecureRandom;
  3. import java.io.*;
  4. import java.util.*;
  5.  
  6.  
  7. class RSAtest
  8. {
  9.         BigInteger p = new BigInteger("11");            // 비밀키 소수 p = 11
  10.     BigInteger q = new BigInteger("17");        // 비밀키 소수 q = 17
  11.         BigInteger d = new BigInteger("23");            // z = (p-1)(q-1) = 160의 소로소로 d는 23을 택하였습니다.
  12.         BigInteger e = new BigInteger("7");             // e * d(7) mod 160 = 1 인 수 e는 7으로 정하였습니다. 공개키
  13.        
  14.         BigInteger n;                                                   // n = p *q 공개키
  15.  
  16.         RSAtest()
  17.         {
  18.                 n = p.multiply(q);      // 공개키 n = p * q
  19.         }
  20.        
  21.         void encrypt_msg(BigInteger txt[])
  22.         {
  23.                 System.out.println("암호화를 진행합니다.");
  24.                 for (int i=0; i < txt.length;i++ )
  25.                 {      
  26.  
  27.                         txt[i] = txt[i].modPow(e, n);
  28.  
  29.                 }
  30.         }
  31.  
  32.         void decrypt_msg(BigInteger txt[])
  33.         {
  34.                 System.out.println("복호화를 진행합니다.");
  35.                 for (int i=0; i < txt.length;i++ )
  36.                 {
  37.                         txt[i] = txt[i].modPow(d, n);
  38.                 }
  39.         }
  40.  
  41.         public static void main(String[] args)
  42.         {
  43.                 RSAtest rsa = new RSAtest();
  44.                 BigInteger[] key;
  45.                 Scanner sc = new Scanner(System.in);
  46.                 String s,b;
  47.  
  48.                 String t;
  49.                 Integer t2;
  50.  
  51.                 System.out.println("평문을 입력해주세요.");
  52.                 s = sc.nextLine();
  53.                
  54.  
  55.                 key = new BigInteger[s.length()];
  56.  
  57.                 for (int i=0;< s.length() ;i++ )
  58.                 {
  59.                         t2 = (int)s.charAt(i);
  60.                         t = t2.toString();
  61.        
  62.                         key[i] = new BigInteger(t);
  63.                 }
  64.                        
  65.                 // 암호화
  66.                 rsa.encrypt_msg(key);
  67.  
  68.  
  69.                 // 복호화
  70.                 rsa.decrypt_msg(key);
  71.  
  72.                 s = key.toString();
  73.  
  74.                 for(int i = 0; i < key.length; i++)
  75.         {
  76.             String s2 = key[i].toString();
  77.             Integer integer = Integer.valueOf(Integer.parseInt(s2));
  78.             int k = integer.intValue();
  79.             System.out.printf("%c"new Object[] {
  80.                 Integer.valueOf(k)
  81.             });
  82.         }
  83.  
  84.                 System.out.println();
  85.                
  86.         }
  87. }




* 참고 사이트


- http://pulsebeat.tistory.com/56

- http://reinliebe.tistory.com/79


저작자 표시
신고

'IT > Java' 카테고리의 다른 글

자바 리플렉션(Java Reflection)  (0) 2017.01.06
자바(Java)로 구현한 RSA 암호학 알고리즘 예제  (0) 2015.11.12
Java로 달력(Calendar) 구현하기  (0) 2015.05.18
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 

아래는 Java로 달력을 구현한 소스코드이다. 2개파일로 나뉘었고, 주석에 자세한 설명을 해놨으니 복붙해서 봐도될듯하다.
1980년 1월 1일(화요일)을 기준으로 지정해준 날짜까지(윤년 포함) 총합을 계산한다 그리고 7로 나눠서 요일을 구한다. 끝

Calender.java

  1. public class Calender {
  2.                                         //미리 기준 연도와 , 기준월을 정해서 출력하는 방법을 선택함.
  3.  
  4.         int base_year = 1980;           // 기준 연도
  5.         int base_month = 1;             // 기준 월 입니다. 실제로는 1980년 1월 1을 기준으로 계산
  6.         int total_sum = 0;              // 기준 년과 월에서 입력받은 날짜까지 총 일수를 여기에 저장
  7.  
  8.         int[] month_table = new int[] { 312831303130313130313031}; //각 월마다 총 일수
  9.        
  10.         public int is_leap_year(int n)  {       //윤년이 있는 년도를 조사하는 함수입니다. 윤년인경우는 1
  11.  
  12.                         if ( n % 4 == 0){       //4의 배수는 윤년
  13.  
  14.                                 if ( n % 100 == 0 ) // 그런데 100의 배수는 윤년이 아님
  15.                                 {
  16.                                         if ( n % 400 == 0 )     //그중에서 400의 배수는 윤년
  17.                                                 return 1;
  18.        
  19.                                         return 0;
  20.                                 }
  21.        
  22.                         return 1;
  23.                 }
  24.                
  25.                 else
  26.                         return 0;
  27.  
  28.         }
  29.  
  30.  
  31.         public int total_to_month(int total)    // 기준 날짜부터 입력받은 날짜까지 총 일수를 구해서 반환,
  32.         {
  33.        
  34.                 boolean CHK = false;            // 무한 루프를 돌리기위한 불리안 값입니다.
  35.                 int i=0;                        // i변수는 월입니다.
  36.                 int cnt=0;                      // 총 일수를 월로 바꾸어서 cnt 저장시킵니다.
  37.                 int chk_leap_year = base_year;  // 총 일수를 월로 바꾸려면 윤년이 있는 날을 고려해야합니다.
  38.                                                 // 기준연도부터 시작해서 윤년을 조사합니다.
  39.                
  40.  
  41.                 while (CHK!=true)
  42.                 {
  43.                         if ( is_leap_year(chk_leap_year) == 1)  //만약 지금 연도가 윤년이라면
  44.                                 month_table[1] = 29;            //2월달은 하루가 더 늘어납니다.
  45.                
  46.        
  47.                         if ( total >= month_table[i] )         // 총 일수가 month_table[]배열의 총일수 보다 작다면,
  48.                         {
  49.                                 total -= month_table[i++]// 총일수를 month_table 배열의 현재 월의 일수로 빼줍니다.
  50.                                                                 // 그리고 i를 증가시킵니다. 즉 다음달로 증가됩니다.
  51.                                 cnt++;                          // 그리고 월이 증가합니다.
  52.                                 if (i==12)                  // 만약 12월이된다면 계절이 변해 다시 제자리로 오는것처럼
  53.                                 {
  54.                                         i-=12;              // 다시 12를빼서 0으로 만들어줍니다.
  55.                                         chk_leap_year++;    //그리고 12개월이 지났으니 연도도 증가시켜줘야함
  56.                                 }
  57.  
  58.                                 month_table[1]= 28;             //윤년을 평년의 해로 바꾸어주어야합니다.
  59.                                
  60.                         }
  61.  
  62.                         else break;
  63.  
  64.                 }
  65.  
  66.                 cnt%=12;        // 위의 무한루프를 통해 총일수를 총월수로 계산됨, 이제 12 나머지 연산을 해주면
  67.                                 // 몇년도 몇월이라는 값으로 바꿀수있습니다.
  68.  
  69.                 return  (cnt+1);        // 그리고 바꾼 월을 반환
  70.  
  71.         }
  72.  
  73.         public int count_leap(int n)            // 기준 연도부터 시작해서 입력받은 연도까지의 윤년이 있는날을 셉니다.
  74.         {
  75.                 int i;          //기준연도를 저장합니다.
  76.                 int cnt=0;      // 윤년의 개수입니다.
  77.  
  78.                 for (i=base_year ; i < (base_year-n) ; i++)
  79.                 // i(기준연도) 부터 입력받은 연도까지 i를 증가시키며 윤년이낀 갯수를 구함.
  80.                 {
  81.  
  82.                         if ( is_leap_year(i) == 1 )     //위에서 구현한 윤년인지를 판단하는 함수를 사용
  83.                         {
  84.                                 cnt++;                  // 윤년이라면 총 윤년의 개수를 증가시킵니다.
  85.                         }
  86.                
  87.                 }
  88.  
  89.                 return cnt;             //카운트한 윤년의 갯수를 리턴합니다.
  90.  
  91.         }
  92.  
  93.         public void convert_to_day(int nYear) {         // 기준 연도부터 입력받은 연도까지 총일수를 구해서 리턴합니다.
  94.                 total_sum  =(( nYear - base_year) * 365)+count_leap((base_year-nYear));
  95.         }
  96.                

  97.         void result(int nYear, int mth)
  98.         {
  99.                 int i,j;                // 카운트를 위한 변수입니다.
  100.                 int d=0;               
  101.                 int year=0;
  102.                 int month;
  103.                 int sum=0;
  104.  
  105.                 int dy = count_leap(base_year-nYear);   //dy는 기준연도부터 현재연도까지 낀 윤년의 갯수입니다.
  106.                 convert_to_day(nYear);  // ★ 우선 기준연도부터 현재 연도까지 년 단위로 총일수를 구합니다. ★
  107.  
  108.                 int day;        // 이변수는 요일을 결정하기위해 존재합니다.예를들어 기준일부터 현재일까지 차이가 7이고
  109.                                 // 기준일이 월요일이면 7로 나눠서 나머지가 0이되니까 월요일임을 알수 있듯이
  110.                                 // day는 숫자로서 요일을 결정할수있습니다.
  111.                 if ( nYear >= base_year)
  112.                 {
  113.  
  114.                         if ( is_leap_year(nYear) == 1)          //윤달이 낀날의 2월은 하루 증가
  115.                                         month_table[1] = 29;
  116.                
  117.                         for ( i =0; i < (mth - base_month ) ;i++)              
  118.                                         total_sum += month_table[i];
  119.                                         // 위에 ★ 에서 기준연도부터 현재연도까지의 일수를 구했습니다.
  120.                                         // 이 for루프를 통해 나머지 기준월부터 현재월까지의 총일수를 구합니다.     
  121.                                         // 즉 이루프를 통해 기준 연도와 월부터 현재 연도와 월까지의 총일수를 구함.
  122.                
  123.                         day = (total_sum+2) % 7;
  124.                       // 현재까지의 총일수를 7의 나머지로 연산해줍니다. 2를 더해준 이유는 1980년도 1월 1일 = 화요일

  125.                         System.out.println("총 일수 = "+total_sum+"윤달이 낀 숫자 = " +dy);
  126.                         month = total_to_month(total_sum); // 입력받은 해당 날짜의 정확한 달을 구해서 저장합니다.
  127.  
  128.                         System.out.println(+month+" 월의 달력");
  129.  
  130.                         System.out.println("일 월     화     수     목     금     토");
  131.  
  132.                         for ( i = 0; i <  day; i++) //day 변수는 요일 입니다. 갯수만큼 \t로 공백을 만들어줍니다.
  133.                                 System.out.print("\t");
  134.  
  135.                         for ( j = 1 ; j <= month_table[month-1]; j++)  
  136.                         // month변수에서 1을 빼준이유는 배열은 0부터 시작하기때문입니다.
  137.                         {                                              
  138.                                 System.out.print(j+"\t");// j를 증가시켜가며 차례데로 날짜를 출력합니다.
  139.  
  140.                                 if (((j+day) % 7) == 0 ) System.out.println(); 
  141.                                 // 그리고 처음 요일을 출력하기위한 공백만큼 계산해서 출력
  142.                         }
  143.  
  144.                         System.out.println();
  145.  
  146.                         month_table[1] = 28;    //윤년이었다면 다시 평년으로 바꾸어줍니다.
  147.  
  148.                 }
  149.         }
  150.  
  151. }


CalenderUser.java


  1. import java.util.Scanner;
  2.  
  3. public class CalenderUser {
  4.         public static void main(String args[])
  5.         {      
  6.                 int year,month;
  7.  
  8.                 Scanner s = new Scanner(System.in);
  9.                 Calender cldUser = new Calender();
  10.                 System.out.println("Year, Month");     
  11.                 year = s.nextInt();
  12.                 month = s.nextInt();// 연도와 월을 입력받습니다.
  13.                 cldUser.result(year,month); // cldUser클래스의 result함수에매개변수
  14.                                             // year, month를 넘겨주면 cldUser클래스가 결과를 출력합니다.
  15.         }
  16. }


컴파일 

javac Calender.java

javac CalenderUser.java


실행

java Calender


실행화면




저작자 표시
신고

'IT > Java' 카테고리의 다른 글

자바 리플렉션(Java Reflection)  (0) 2017.01.06
자바(Java)로 구현한 RSA 암호학 알고리즘 예제  (0) 2015.11.12
Java로 달력(Calendar) 구현하기  (0) 2015.05.18
Posted by 캐스피


티스토리 툴바