一、Java语言基础
问题1:请解释Java中的封装、继承和多态。
答案:
- 封装(Encapsulation):是面向对象的三大特性之一,通过将数据和操作数据的方法绑定在同一个类中,并通过访问修饰符控制外部对这些数据成员和方法的访问权限,实现了数据的安全性和隐藏性,同时降低了模块间的耦合度。
例如:
public class Student {
private String name; // 私有化属性,实现封装
public void setName(String name) { // 提供公共方法设置姓名
this.name = name;
}
public String getName() { // 提供公共方法获取姓名
return name;
}
}
- 继承(Inheritance):一个类可以从已有的类中派生出新的类,新类被称为子类或派生类,被继承的类称为父类或基类。子类可以继承父类的所有非私有属性和方法,并且可以根据需要进行扩展或者重写。
例如:
public class Person {
protected String name;
public void walk() {
System.out.println("Person is walking.");
}
}
public class Student extends Person {
private int grade;
@Override
public void walk() {
super.walk();
System.out.println("Student is walking to school.");
}
}
- 多态(Polymorphism):指同一接口表现出多种不同的形式。Java中有两种多态方式:静态多态(编译时多态,通过方法重载实现)和动态多态(运行时多态,通过继承和接口实现)。在运行时,一个引用变量可以指向多个不同类型的实例,实际执行的方法取决于引用变量所指向的对象的实际类型。
例如:
public class Animal {
public void sound() {
System.out.println("The animal makes a sound");
}
}
public class Dog extends Animal {
@Override
public void sound() {
System.out.println("The dog barks");
}
}
// 示例使用
Animal animal = new Dog(); // 创建Dog对象并赋值给Animal类型的引用
animal.sound(); // 输出"The dog barks",体现了运行时多态
问题2:请简述Java内存模型以及JVM内存区域划分。
答案: Java内存模型(Java Memory Model, JMM)定义了共享变量的可见性和原子性规则,保证并发环境下的正确性。而JVM内存区域主要划分为以下几个部分:
-
程序计数器(Program Counter Register):线程私有,存储当前正在执行的字节码指令地址。
-
虚拟机栈(Java Virtual Machine Stacks):线程私有,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每个方法调用都会创建一个新的栈帧。
-
本地方法栈(Native Method Stacks):与虚拟机栈类似,但服务于native方法。
-
堆(Heap):所有线程共享的一块内存区域,用于存放对象实例。垃圾回收的主要区域。
-
方法区(Method Area)/元空间(Metaspace):所有线程共享,存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。从Java 8开始,永久代被元空间取代。
-
直接内存(Direct Memory):并非JVM规范规定的内存区域,但在NIO中会用到,它是在Java堆外分配的,可通过Unsafe类或Native函数直接分配和释放。
问题3:Java中的四种访问修饰符分别是什么,以及它们的作用范围?
答案: 在Java中,有四种访问修饰符用于控制类、成员变量和方法的可见性
-
public class MyClass { public int myPublicVariable; public void myPublicMethod() {} }
-
protected:受保护访问权限,对同一包内的其他类及不同包但继承了此类的所有子类可见。这意味着,在不同的包中,只有子类可以访问其父类的protected成员。
package com.example.parent; public class ParentClass { protected String myProtectedVariable; protected void myProtectedMethod() {} } package com.example.child; import com.example.parent.ParentClass; public class ChildClass extends ParentClass { // 可以访问ParentClass的protected成员 }
-
(默认)无修饰符(也称为package-private或default):包访问权限,仅对同一包内的其他类可见。如果一个类或成员没有显式声明任何访问修饰符,则它具有包访问权限。
package com.example.myPackage; public class MyDefaultClass { String myPackagePrivateVariable; // 默认访问权限 void myPackagePrivateMethod() {} // 默认访问权限 } // 在同包下的另一个类可以访问 package com.example.myPackage; public class AnotherClassInSamePackage { public void accessDefaultMembers(MyDefaultClass obj) { System.out.println(obj.myPackagePrivateVariable); obj.myPackagePrivateMethod(); } } // 不同包下则无法直接访问 package com.example.anotherPackage; import com.example.myPackage.MyDefaultClass; public class AnotherClassInDifferentPackage { public void cannotAccessDefaultMembers(MyDefaultClass obj) { // 下面这行代码会编译错误,因为myPackagePrivateVariable是包访问权限 // System.out.println(obj.myPackagePrivateVariable); } }
-
private:私有访问权限,只对自己所在的类可见,外部类不能直接访问。主要用于隐藏内部实现细节,提高封装性。
public class MyClass { private int myPrivateVariable; private void myPrivateMethod() {} // 提供公共方法来间接访问私有成员 public int getMyPrivateVariable() { return this.myPrivateVariable; } public void callMyPrivateMethod() { this.myPrivateMethod(); } }
理解并合理运用这些访问修饰符有助于设计出具有良好封装性和可维护性的程序结构。
问题4:请简述Java中对象的生命周期,并描述垃圾回收机制是如何工作的。
答案: Java对象的生命周期通常包括以下几个阶段:
-
创建阶段:通过new关键字创建对象,内存分配发生在堆空间,初始化完成后对象进入就绪状态。
-
使用阶段:对象被引用,执行其方法,改变其属性值等。
-
不可达阶段:当对象不再被任何强引用所指向时,即成为不可达对象。JVM的垃圾回收器将判断这个对象是否还可能被再次引用。
-
垃圾收集阶段:垃圾回收器检测到某个对象为不可达对象后,会在适当的时机对其进行回收操作,释放其所占用的内存资源。
Java的垃圾回收机制主要基于可达性分析算法,即从一系列GC Roots对象出发,向下搜索,凡是从GC Roots开始无法到达的对象都被认为是不可达对象,可被垃圾回收器回收。常见的GC Roots包括:虚拟机栈中引用的对象、方法区中静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象等。
垃圾回收的具体过程包括年轻代GC(Minor GC)和老年代GC(Major/Full GC),涉及到新生代和老年代的划分,以及各种垃圾回收器(如Serial、Parallel、CMS、G1等)的工作原理与策略,此处不做详述。了解这些内容有助于优化程序性能,避免内存溢出等问题。
理解以上内容对于Java开发者来说至关重要,有助于写出更高效、安全的代码,也便于在面试过程中从容应对相关问题。