답변:
개인 필드에 액세스하려면 클래스의 선언 된 필드 에서 가져 와서 액세스 할 수 있어야합니다.
Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException
편집 : aperkins 가 댓글을 달았 듯이 필드에 액세스하고 액세스 가능으로 설정하고 값을 검색하면 Exception
s 를 던질 수 있습니다 . 단지 확인 해야 할 예외는 위에 설명되어 있습니다.
는 NoSuchFieldException
당신이 선언 된 필드에 해당하지 않은 이름으로 필드에 대한 요구한다면 던져 질 것이다.
obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
이 IllegalAccessException
분야에 액세스 할 수없는 있다면 비공개이며, 놓치고를 통해 액세스 가능하지 않은 경우 (예를 들어, 던져 질 것이다 f.setAccessible(true)
선을.
RuntimeException
발생 될 수 있습니다들 중 하나입니다 SecurityException
(JVM의이 경우의 SecurityManager
당신이 필드의 접근성을 변경할 수 없습니다), 또는 IllegalArgumentException
당신이하지 필드의 클래스의 유형의 개체에 액세스에게 필드를 시도하고있는 경우, S :
f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
getDeclaredField()
필드가 부모 클래스에 정의 된 경우 필드를 찾지 못합니다. 또한 getDeclaredField()
일치하는 항목을 찾거나 (나중에 호출 할 수 있음 setAccessible(true)
) 또는 도달 할 때까지 각 클래스를 호출하여 부모 클래스 계층 구조를 반복해야 합니다 Object
.
private
필드 와 다른 점이 있습니다. 우발적 인 액세스는 불가능합니다.
FieldUtils
Apache Commons-lang3에서 시도하십시오 .
FieldUtils.readField(object, fieldName, true);
리플렉션 만이 문제를 해결할 수있는 유일한 방법은 아닙니다 (클래스 / 컴포넌트의 개인 기능 / 행동에 액세스하는 것임)
다른 해결책은 .jar에서 클래스를 추출하고 Jode 또는 Jad를 사용하여 디 컴파일 하고 필드를 변경하거나 액세서를 추가 한 다음 원래 .jar에 대해 다시 컴파일하는 것입니다. 그런 다음 새 .class를 .jar
클래스 경로 앞에 놓거나에 다시 삽입하십시오 .jar
. jar 유틸리티를 사용하면 기존 .jar을 추출하고 다시 삽입 할 수 있습니다.
아래에 언급 된 것처럼, 이는 단순히 필드에 액세스 / 변경하는 것이 아니라 개인 상태에 액세스 / 변경하는 더 넓은 문제를 해결합니다.
이것은 필요 .jar
하지 물론, 서명 할 수 있습니다.
아직 언급되지 않은 다른 옵션 : Groovy 사용하십시오 . Groovy를 사용하면 언어 디자인의 부작용으로 프라이빗 인스턴스 변수에 액세스 할 수 있습니다. 현장에 게터가 있든 없든 상관없이
def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()
Java 에서 Reflection을 사용하면 private/public
한 클래스의 모든 필드와 메소드를 다른 클래스에 액세스 할 수 있지만 섹션 단점 의 Oracle 문서 에 따라 다음과 같은 권장 사항이 있습니다.
"리플렉션을 사용하면 개인 필드 및 메소드에 액세스하는 것과 같이 비 반사 코드에서 불법적 인 작업을 코드에서 수행 할 수 있으므로 리플렉션을 사용하면 예기치 않은 부작용이 발생하여 코드가 제대로 작동하지 않아 이식성이 떨어질 수 있습니다. "추상화를 중단하고 플랫폼 업그레이드로 동작이 변경 될 수 있습니다."
다음은 리플렉션의 기본 개념을 보여주는 코드 스냅 샷입니다.
Reflection1.java
public class Reflection1{
private int i = 10;
public void methoda()
{
System.out.println("method1");
}
public void methodb()
{
System.out.println("method2");
}
public void methodc()
{
System.out.println("method3");
}
}
Reflection2.java
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection2{
public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Method[] mthd = Reflection1.class.getMethods(); // for axis the methods
Field[] fld = Reflection1.class.getDeclaredFields(); // for axis the fields
// Loop for get all the methods in class
for(Method mthd1:mthd)
{
System.out.println("method :"+mthd1.getName());
System.out.println("parametes :"+mthd1.getReturnType());
}
// Loop for get all the Field in class
for(Field fld1:fld)
{
fld1.setAccessible(true);
System.out.println("field :"+fld1.getName());
System.out.println("type :"+fld1.getType());
System.out.println("value :"+fld1.getInt(new Reflaction1()));
}
}
}
그것이 도움이되기를 바랍니다.
oxbow_lakes가 언급했듯이 리플렉션을 사용하여 액세스 제한을 피할 수 있습니다 (SecurityManager에서 허용한다고 가정).
즉,이 클래스가 너무 나쁘게 설계되어 해커에게 의지하게되면 대안을 찾아야 할 것입니다. 이 작은 해킹으로 인해 몇 시간이 절약 될 수 있지만 비용이 얼마나 들까 요?
Soot Java Optimization 프레임 워크를 사용하여 바이트 코드를 직접 수정하십시오. http://www.sable.mcgill.ca/soot/
그을음은 Java로 완전히 작성되었으며 새로운 Java 버전에서 작동합니다.
다음을 수행해야합니다.
private static Field getField(Class<?> cls, String fieldName) {
for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
try {
final Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (final NoSuchFieldException e) {
// Try parent
} catch (Exception e) {
throw new IllegalArgumentException(
"Cannot access field " + cls.getName() + "." + fieldName, e);
}
}
throw new IllegalArgumentException(
"Cannot find field " + cls.getName() + "." + fieldName);
}
Spring을 사용하는 경우 ReflectionTestUtils 는 최소한의 노력으로 여기에 도움이되는 편리한 도구를 제공합니다. "단위 및 통합 테스트 시나리오에서 사용하기 위해" 설명되어 있습니다 . ReflectionUtils 라는 비슷한 클래스도 있지만 "내부 용으로 만 사용" 으로 설명되어 있습니다. 이것이 의미하는 바에 대한 해석 은 이 답변 을 참조하십시오 .
게시 된 예제를 해결하려면 다음을 수행하십시오.
Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
ReflectionTestUtils
내 경험 에서처럼 테스트 상황에서 이런 종류의 작업을 수행하는 데 필요한 예제를 계속 사용 했습니다.)
리플렉션에 대한 추가 참고 사항 : 일부 특수한 경우, 동일한 이름을 가진 여러 클래스가 다른 패키지에 존재할 때 최상위 답변에 사용 된 리플렉션이 객체에서 올바른 클래스를 선택하지 못할 수 있습니다. 따라서 객체의 package.class가 무엇인지 알고 있다면 다음과 같이 전용 필드 값에 액세스하는 것이 좋습니다.
org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0);
Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver");
f.setAccessible(true);
Solver s = (Solver) f.get(ll);
(이것은 저에게 효과적이지 않은 예제 수업입니다)
직접적이고 타입이 안전한 Java 리플렉션을 위해 Manifold의 @JailBreak 를 사용할 수 있습니다 .
@JailBreak Foo foo = new Foo();
foo.stuffIWant = "123;
public class Foo {
private String stuffIWant;
}
@JailBreak
의 계층 구조에 foo
있는 모든 멤버에 직접 액세스 할 수 있도록 컴파일러에서 로컬 변수를 잠금 해제합니다 Foo
.
마찬가지로 jailbreak () 확장 메소드를 일회용으로 사용할 수 있습니다.
foo.jailbreak().stuffIWant = "123";
이 jailbreak()
방법을 통해 Foo
의 계층 구조에 있는 모든 멤버에 액세스 할 수 있습니다 .
두 경우 모두, 컴파일러는 공개 필드처럼 사용자에게 안전하게 필드 액세스를 해결하는 반면 매니 폴드는 효율적인 리플렉션 코드를 생성합니다.
매니 폴드 에 대해 자세히 알아보십시오 .
XrayInterface 도구를 사용하면 매우 쉽습니다 . 누락 된 게터 / 세터를 정의하십시오. 예 :
interface BetterDesigned {
Hashtable getStuffIWant(); //is mapped by convention to stuffIWant
}
당신의 열악한 디자인 프로젝트를 xray :
IWasDesignedPoorly obj = new IWasDesignedPoorly();
BetterDesigned better = ...;
System.out.println(better.getStuffIWant());
내부적으로 이것은 반사에 의존합니다.
데이터를 설정 / 가져 오려는 calass가 자신의 클래스 중 하나 인 경우의 문제를 해결하십시오.
그냥 생성 public setter(Field f, Object value)
하고 public Object getter(Field f)
그 위해를. 이 멤버 함수 내에서 직접 보안 검사를 수행 할 수도 있습니다. 예를 들어 세터의 경우 :
class myClassName {
private String aString;
public set(Field field, Object value) {
// (A) do some checkings here for security
// (B) set the value
field.set(this, value);
}
}
물론, 지금은 알아 내야 java.lang.reflect.Field
대한 sString
이전 필드 값의 설정에.
이 기술은 일반적인 ResultSet-to-and-from-model-mapper에서 사용합니다.