Java常见面试题

171 阅读2分钟

1.final修饰的变量永远不会被改变吗?有什么例外吗?

在一般情况下,final修饰的变量是无法被改变的。但在反射中是可以修改非基本数据类型和String类型变量的值的。

反射获取final修饰的变量

例有此实体类:

public class User {
    private final String name = "Leslie";
    private final Student student = new Student();

    public String getName() {
        return name;
    }

    public Student getStudent() {
        return student;
    }
}

利用反射获取name:

User user = new User();
Class clz = User.class;
Field field = null;
try{
    field=clz.getDeclaredField("name");
    field.setAccessible(true);//关闭安全检查,保证可访问
    field.set(user,"Eason");
    System.out.println(user.getName());
}catch(NoSuchFieldException e){
    e.printStackTrace();
}catch(IllegalAccessException e){
    e.printStackTrace();
}

打印出来,仍为:“Leslie”,我们继续更改student:

field = clz.getDeclaredField("student");
field.setAccessible(true);
field.set(user, new Student());

打印出来两个Student的内存地址不同,说明改变final型成功。

为什么String类型的不能修改,但Student就可以被修改呢?因为在基本数据类型和String类型中,JVM做了内联优化,例:在此段代码中,JVM会将
public String getName() { return name; }
内联优化为:
public String getName() { return "Leslie"; }
因此在修改name时并不会对getName()的返回值造成变化

有的朋友可能会问:若是直接获取name的值呢?例如:

//修改为public
public final String name = "Leslie";

//反射修改后,打印user.name
field=clz.getDeclaredField("name");
field.setAccessible(true);//关闭安全检查,保证可访问
field.set(user,"Eason");
System.out.println(user.name);

打印出来结果仍为:“Leslie”,因为
System.out.println(user.name);
在经过编译后会变为
System.out.println("Leslie");
那就没有办法获取到改变后的值了吗?
可以通过Field.get(Object obj)方法获取:
System.out.println("修改后"+field.get(user));

反射获取static修饰的变量

static变量是在类实例化之前就被初始化了(类的初始化阶段),因此静态变量是跟着类本身走的,跟具体的对象无关,所以我们获取变量就不需要传入对象,直接传入null即可:

public class User {
    public static String name;
}

field2 = clz.getDeclaredField("name");
field2.setAccessible(true);
//获取静态变量
Object getname=field2.get(null);
System.out.println("修改前"+getname);

//修改静态变量
field2.set(null, "Eason");
System.out.println("修改后"+User.name);  

如上:Field.get(null) 可以获取静态变量。
Field.set(null,object) 可以修改静态变量。