面试题答案
一键面试1. 编译期
- 成员变量:在编译时,Java编译器会为每个类生成对应的字节码文件。成员变量在字节码文件中以字段(field)的形式存在。编译器会根据成员变量的类型、名称和访问修饰符等信息,在字节码中生成相应的字段表项。例如,对于一个简单的类
public class MyClass { private int myVar; }
,字节码中会有一个关于myVar
字段的表项,记录其名称、类型(int
)和访问修饰符(private
)等信息。 - 方法:方法在编译时会生成对应的字节码指令序列。方法的字节码包含在方法表项中,其中记录了方法的名称、参数列表、返回类型、访问修饰符以及具体的字节码指令。例如,对于
public void myMethod() { }
,字节码中会有相应的方法表项,包含上述信息以及空方法体对应的字节码指令(可能是return
指令)。
2. 加载期
- 成员变量:当类被类加载器加载时,成员变量会随着类的加载而在方法区中分配内存。对于类变量(
static
修饰的变量),它们在类加载完成后就会被初始化并分配内存,且在整个程序运行期间只有一份实例。而实例变量(非static
变量)则在创建类的实例时才会在堆内存中分配内存。 - 方法:方法在类加载时,其字节码会被加载到方法区中。方法区存储了类的结构信息,包括方法的字节码、常量池等。每个类的方法在方法区中只有一份共享的定义,不同实例调用方法时,实际上是执行方法区中的同一套字节码指令。
3. 运行期
- 成员变量:
- 实例变量:当创建类的实例时,实例变量在堆内存中分配空间,并根据其类型进行默认初始化。例如
MyClass obj = new MyClass();
,obj
的实例变量myVar
会在堆中分配4字节(int
类型)空间,并初始化为0。当通过实例访问实例变量时,如obj.myVar
,会根据对象在堆中的内存地址找到对应的实例变量。 - 类变量:类变量在类加载后就存在于方法区,可通过类名直接访问,如
MyClass.staticVar
。在运行时,对类变量的访问直接在方法区中进行。
- 实例变量:当创建类的实例时,实例变量在堆内存中分配空间,并根据其类型进行默认初始化。例如
- 方法:
- 实例方法:当通过实例调用实例方法时,如
obj.myMethod()
,首先会根据对象的内存地址找到对象的实际类型,然后在该类型的方法表中查找对应的方法,找到后执行方法区中该方法的字节码指令。在执行过程中,方法可以访问实例变量和其他类成员。 - 类方法(
static
方法):类方法可通过类名直接调用,如MyClass.staticMethod()
。由于类方法不依赖于实例,在调用时直接在方法区中找到该类方法的字节码指令并执行。
- 实例方法:当通过实例调用实例方法时,如
4. 成员变量访问修饰符在字节码中的体现
private
:在字节码中,private
修饰的字段在字段表的访问标志中会设置ACC_PRIVATE
标志位。例如,使用javap -v MyClass
反编译包含private int myVar;
的类时,在字段的描述信息中会看到ACC_PRIVATE
,表示该字段是私有的,只能在类内部访问。public
:public
修饰的字段在字段表的访问标志中设置ACC_PUBLIC
标志位。在反编译结果中可看到该标志,表明该字段可以被任何类访问。protected
:protected
修饰的字段在字段表的访问标志中设置ACC_PROTECTED
标志位。表示该字段可以被同一包内的类以及子类访问。- 默认(包访问权限):没有明确访问修饰符的字段,在字节码中字段表的访问标志没有上述特定标志位,其访问权限限制在同一包内。
示例代码及反编译分析
public class AccessModifiersExample {
private int privateVar;
public int publicVar;
protected int protectedVar;
int packageVar;
public AccessModifiersExample() {
privateVar = 0;
publicVar = 0;
protectedVar = 0;
packageVar = 0;
}
public void printVars() {
System.out.println("privateVar: " + privateVar);
System.out.println("publicVar: " + publicVar);
System.out.println("protectedVar: " + protectedVar);
System.out.println("packageVar: " + packageVar);
}
}
使用javap -v AccessModifiersExample
反编译该类,在输出结果中:
- 对于
privateVar
字段,会看到ACC_PRIVATE
标志,表明其私有访问权限。 - 对于
publicVar
字段,会看到ACC_PUBLIC
标志,表明其公有访问权限。 - 对于
protectedVar
字段,会看到ACC_PROTECTED
标志,表明其受保护访问权限。 - 对于
packageVar
字段,没有特定访问标志,表明其包访问权限。 方法部分同理,public
修饰的printVars
方法在方法表的访问标志中会有ACC_PUBLIC
标志。