In order to access private fields, you need to get them from the class's declared fields and then make them accessible:
Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException
EDIT: as has been commented by aperkins, both accessing the field, setting it as accessible and retrieving the value can throw Exceptions, although the only checked exceptions you need to be mindful of are commented above.
The NoSuchFieldException would be thrown if you asked for a field by a name which did not correspond to a declared field.
obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
The IllegalAccessException would be thrown if the field was not accessible (for example, if it is private and has not been made accessible via missing out the f.setAccessible(true) line.
The RuntimeExceptions which may be thrown are either SecurityExceptions (if the JVM's SecurityManager will not allow you to change a field's accessibility), or IllegalArgumentExceptions, if you try and access the field on an object not of the field's class's type:
f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
Answer from oxbow_lakes on Stack OverflowIn order to access private fields, you need to get them from the class's declared fields and then make them accessible:
Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException
EDIT: as has been commented by aperkins, both accessing the field, setting it as accessible and retrieving the value can throw Exceptions, although the only checked exceptions you need to be mindful of are commented above.
The NoSuchFieldException would be thrown if you asked for a field by a name which did not correspond to a declared field.
obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
The IllegalAccessException would be thrown if the field was not accessible (for example, if it is private and has not been made accessible via missing out the f.setAccessible(true) line.
The RuntimeExceptions which may be thrown are either SecurityExceptions (if the JVM's SecurityManager will not allow you to change a field's accessibility), or IllegalArgumentExceptions, if you try and access the field on an object not of the field's class's type:
f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
Try FieldUtils from Apache commons-lang3:
FieldUtils.readField(object, fieldName, true);
P.S. In my opinion, reflection is evil.
Videos
Yes, it absolutely is - assuming you've got the appropriate security permissions. Use Field.setAccessible(true) first if you're accessing it from a different class.
import java.lang.reflect.*;
class Other
{
private String str;
public void setStr(String value)
{
str = value;
}
}
class Test
{
public static void main(String[] args)
// Just for the ease of a throwaway test. Don't
// do this normally!
throws Exception
{
Other t = new Other();
t.setStr("hi");
Field field = Other.class.getDeclaredField("str");
field.setAccessible(true);
Object value = field.get(t);
System.out.println(value);
}
}
And no, you shouldn't normally do this... it's subverting the intentions of the original author of the class. For example, there may well be validation applied in any situation where the field can normally be set, or other fields may be changed at the same time. You're effectively violating the intended level of encapsulation.
Yes.
Field f = Test.class.getDeclaredField("str");
f.setAccessible(true);//Very important, this allows the setting to work.
String value = (String) f.get(object);
Then you use the field object to get the value on an instance of the class.
Note that get method is often confusing for people. You have the field, but you don't have an instance of the object. You have to pass that to the get method
You have do like this,
public class A {
private B b = new B();
class B {
private String value = "String";
}
}
public class ClassB {
public static void main(String args[]) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
A obj = new A();
Field f = obj.getClass().getDeclaredField("b");
f.setAccessible(true);
A.B b = (B) f.get(obj);
Field f2 = b.getClass().getDeclaredField("value");
f2.setAccessible(true);
String value = (String) f2.get(b);
System.out.println(value);
}
}
What you are missing is to setAccessible(true) to inner class field.
As a first, in your example field b is null. Is this correct? So, you try to get class of null.
As a second, in your example you use inner classes and there is a specific langugage mechanizm. You can create instance of class B only by some instance of class A. And all instances of class B has access to private field of it's parrent (class A). As in this example.
class OuterClass
{
// static member
static int outer_x = 10;
// instance(non-static) member
int outer_y = 20;
// private member
private int outer_private = 30;
// inner class
class InnerClass
{
void display()
{
// can access static member of outer class
System.out.println("outer_x = " + outer_x);
// can also access non-static member of outer class
System.out.println("outer_y = " + outer_y);
// can also access private member of outer class
System.out.println("outer_private = " + outer_private);
}
}
}
// Driver class
public class InnerClassDemo
{
public static void main(String[] args)
{
// accessing an inner class
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
innerObject.display();
}
}
May be inner classes can solves your problem? (You can read abou it here https://www.geeksforgeeks.org/nested-classes-java/) Then reflecsoin is not needed.
1) How can I access the private methods and the private data members?
You can do it with a little help of the setAccessible(true) method:
class Dummy{
private void foo(){
System.out.println("hello foo()");
}
private int i = 10;
}
class Test{
public static void main(String[] args) throws Exception {
Dummy d = new Dummy();
/*--- [INVOKING PRIVATE METHOD] ---*/
Method m = Dummy.class.getDeclaredMethod("foo");
//m.invoke(d); // Exception java.lang.IllegalAccessException
m.setAccessible(true);//Abracadabra
m.invoke(d); // Now it's OK
/*--- [GETING VALUE FROM PRIVATE FIELD] ---*/
Field f = Dummy.class.getDeclaredField("i");
//System.out.println(f.get(d)); // Not accessible now
f.setAccessible(true); // Abracadabra
System.out.println(f.get(d)); // Now it's OK
/*--- [SETTING VALUE OF PRIVATE FIELD] ---*/
Field f2 = Dummy.class.getDeclaredField("i");
//f2.set(d,20); // Not accessible now
f2.setAccessible(true); // Abracadabra
f2.set(d, 20); // Now it's OK
System.out.println(f2.get(d));
}
}
2) Is it possible to access a local variable via reflection?
No. Local variables cannot be accessed outside of a block in which they were created (someone could say that you can assign such a variable to a field like field = localVariable; and later access such a field via reflection, but this way we will be accessing the value, not the variable).
3) Is there any way to prevent anyone from accessing private constructors, methods, and data members?
I think for constructors or methods you could use stacktrace to check if it was invoked by Reflection.
For fields I can't find a solution to prevent accessing them via reflection.
[WARNING: This is not approved by anyone. I just wrote it inspired by your question.]
class Dummy {
private void safeMethod() {
StackTraceElement[] st = new Exception().getStackTrace();
// If a method was invoked by reflection, the stack trace would be similar
// to something like this:
/*
java.lang.Exception
at package1.b.Dummy.safeMethod(SomeClass.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
-> at java.lang.reflect.Method.invoke(Method.java:601)
at package1.b.Test.main(SomeClass.java:65)
*/
//5th line marked by "->" is interesting one so I will try to use that info
if (st.length > 5 &&
st[4].getClassName().equals("java.lang.reflect.Method"))
throw new RuntimeException("safeMethod() is accessible only by Dummy object");
// Now normal code of method
System.out.println("code of safe method");
}
// I will check if it is possible to normally use that method inside this class
public void trySafeMethod(){
safeMethod();
}
Dummy() {
safeMethod();
}
}
class Dummy1 extends Dummy {}
class Test {
public static void main(String[] args) throws Exception {
Dummy1 d1 = new Dummy1(); // safeMethod can be invoked inside a superclass constructor
d1.trySafeMethod(); // safeMethod can be invoked inside other Dummy class methods
System.out.println("-------------------");
// Let's check if it is possible to invoke it via reflection
Method m2 = Dummy.class.getDeclaredMethod("safeMethod");
// m.invoke(d);//exception java.lang.IllegalAccessException
m2.setAccessible(true);
m2.invoke(d1);
}
}
Output from Test main method:
code of safe method
code of safe method
-------------------
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at package1.b.Test.main(MyClass2.java:87)
Caused by: java.lang.RuntimeException: method safeMethod() is accessible only by Dummy object
at package1.b.Dummy.safeMethod(MyClass2.java:54)
... 5 more
- Using the method shown in the answer you linked to: setAccessible(true), which is a method of the superclass of Field, Constructor and Method.
- No.
- No, unless the code runs in a JVM you control, where you install a security manager. But if you give someone a jar file, and he uses the classes from this jar file, he'll be able to access everything.
To access a private field you need to set Field::setAccessible to true. You can pull the field off the super class. This code works:
CopyClass<?> clazz = Child.class;
Object cc = clazz.newInstance();
Field f1 = cc.getClass().getSuperclass().getDeclaredField("a_field");
f1.setAccessible(true);
f1.set(cc, "reflecting on life");
String str1 = (String) f1.get(cc);
System.out.println("field: " + str1);
Using FieldUtils from the Apache Commons Lang 3:
CopyFieldUtils.writeField(childInstance, "a_field", "Hello", true);
The true forces it to set, even if the field is private.