JAVA15

  • 문자열 처리

문자열 붙이기 과거에는 "" +"" 혹은 StringBuilder 혹은 StringBuffer 객체를 이용해서 무한 append 했었죠.

String html = 
    "<html>\n" +
    "<body>\n"+
    "  <h1>Java 15 새로운 문자열 붙이기...완전 좋네요!</h1>\n"+
    "  <p>didispace.com</p>\n"+
    "</body>\n"+
    "</html>\n";
StringBuilder sb = new StringBuilder();
sb.append("abc");
sb.append("def");
sb.append("ghi");
StringBuffer sb = new StringBuffer();
sb.append("abc");
sb.append("def");
sb.append("ghi");

하지만 JAVA 15부터 아래와 같이 쓸수 있게 되었네요. "" 아닌 """ (3개)를 쓰면 됩니다. 줄바꿈 자유롭게 할수 있습니다.

String html = """
    <html>
    <body>
      <h1>Java 15 새로운 문자열 붙이기...완전 좋네요!</h1>
      <p>didispace.com</p>
    </body>
    </html>
    """;

히든 클래스

다른 class의 bytecode에서 직접 사용할 수 없는 class이다. Hidden class는 runtime에 class를 생성하고 reflection을 통해 간접적으로 사용하는 framework에서 사용하기 위한 것이다. hidden class는 access 제어 중첩의 member로 정의될 수 있으며 다른 class와 독립적으로 unload 될 수 있다.

일반 클래스 하나 생성해 보자

public class JEP371HiddenClasses {

    public static String hello() {
        return "https://rainsister.tistory.com";
    }

}

해당 클래스를 컴파일 하고 나서

String filePath = "JEP371HiddenClasses.class";
byte[] b = Files.readAllBytes(Paths.get(filePath));
log.info(Base64.getEncoder().encodeToString(b));

아래와 같은 코드(문자열)을 얻을 수 있다.

yv66vgAAAD0AFAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCAAIAQAZaHR0cHM6Ly93d3cuZGlkaXNwYWNlLmNvbQcACgEALmNvbS9kaWRpc3BhY2UvZGVidWcvamF2YTE1L0pFUDM3MUhpZGRlbkNsYXNzZXMBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAMExjb20vZGlkaXNwYWNlL2RlYnVnL2phdmExNS9KRVAzNzFIaWRkZW5DbGFzc2VzOwEABWhlbGxvAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAYSkVQMzcxSGlkZGVuQ2xhc3Nlcy5qYXZhACEACQACAAAAAAACAAEABQAGAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAAAwANAAAADAABAAAABQAOAA8AAAAJABAAEQABAAsAAAAbAAEAAAAAAAMSB7AAAAABAAwAAAAGAAEAAAAGAAEAEgAAAAIAEw==

리텐션(Retention)을 이용하여 위에서 작성한 클래스를 로등하여 히든클래스의 hello함수를 호출할수 있다.

@Test
void testHiddenClasses() throws Throwable {
  // 1.히든클래스 로딩 
  String CLASS_INFO = "yv66vgAAAD0AFAoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCAAIAQAZaHR0cHM6Ly93d3cuZGlkaXNwYWNlLmNvbQcACgEALmNvbS9kaWRpc3BhY2UvZGVidWcvamF2YTE1L0pFUDM3MUhpZGRlbkNsYXNzZXMBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAMExjb20vZGlkaXNwYWNlL2RlYnVnL2phdmExNS9KRVAzNzFIaWRkZW5DbGFzc2VzOwEABWhlbGxvAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQAYSkVQMzcxSGlkZGVuQ2xhc3Nlcy5qYXZhACEACQACAAAAAAACAAEABQAGAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAAAwANAAAADAABAAAABQAOAA8AAAAJABAAEQABAAsAAAAbAAEAAAAAAAMSB7AAAAABAAwAAAAGAAEAAAAGAAEAEgAAAAIAEw==";
  byte[] classInBytes = getDecoder().decode(CLASS_INFO);

  Class<?> proxy = MethodHandles.lookup()
    .defineHiddenClass(classInBytes, true, MethodHandles.Lookup.ClassOption.NESTMATE)
    .lookupClass();

  // class name 
  log.info(proxy.getName());
  // class에 어떤 함수가 있는지 보여주기
  for(Method method : proxy.getDeclaredMethods()) {
    log.info(method.getName());
  }
  // 2. hello함수 호출
  MethodHandle mh = MethodHandles.lookup().findStatic(proxy, "hello", MethodType.methodType(String.class));
  String result = (String) mh.invokeExact();
  log.info(result);
}

여기서 CLASS_INFO 는 아까 출력된 문자열들이다.

일단 Base64 로 해당 코드를 복호화하고 리텐션을 이용하여 히든클래스의 프록시를 생성한다.

Class<?> proxy = MethodHandles.lookup()
    .defineHiddenClass(classInBytes, true, MethodHandles.Lookup.ClassOption.NESTMATE)
    .lookupClass();

이분을 유심히 살펴보면 히든클래스 생성이랑 ,일반 클래스 생성차이를 알수 있다.

  • bytes:jvm 규격의 바이트 코드.

  • initialize: 클래스 코기화 진행 여부.

  • options:java 클래스 타입 -> 여기를 java.lang.invoke.MethodHandles.Lookup.ClassOption 확인하면 됨.

Last updated