面向对象基础小结
面向对象和面向过程的区别
两者的主要区别在于解决问题的方式不同:
- 面向过程:是把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。
- 面向对象:会先抽象出对象,然后用对象执行方法的方式解决问题。
面向对象开发的程序一般更易维护、易复用、易扩展。
求圆的面积和周长的示例
面向过程:
直接定义圆的半径,并使用该半径直接计算出圆的面积和周长(即将问题的过程拆分成了两个方法来解决)。
public class Main {
public static void main(String[] args) {
// 定义圆的半径
double radius = 3.0;
// 计算圆的面积和周长
double area = Math.PI * radius * radius;
double perimeter = 2 * Math.PI * radius;
// 输出圆的面积和周长
System.out.println("圆的面积为:" + area);
System.out.println("圆的周长为:" + perimeter);
}
}
面向对象:
定义一个 Circle
类来表示圆(即先抽象出一个对象),该类包含了圆的半径属性和计算面积、周长的方法(用对象的方法解决问题)。
public class Circle {
// 定义圆的半径
private double radius;
// 构造函数
public Circle(double radius) {
this.radius = radius;
}
// 计算圆的面积
public double getArea() {
return Math.PI * radius * radius;
}
// 计算圆的周长
public double getPerimeter() {
return 2 * Math.PI * radius;
}
public static void main(String[] args) {
// 创建一个半径为3的圆
Circle circle = new Circle(3.0);
// 输出圆的面积和周长
System.out.println("圆的面积为:" + circle.getArea());
System.out.println("圆的周长为:" + circle.getPerimeter());
}
}
创建一个对象用什么运算符?对象实体与对象引用有何不同?
用 new
运算符。
- new 创建对象实例(对象实例在堆内存中),
- 对象引用指向对象实例(对象引用存放在栈内存中)。
- 一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)
- 一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球)
对象的相等和引用相等的区别
- 对象的相等一般比较的是内存中存放的内容是否相等。
- 引用相等一般比较的是他们指向的内存地址是否相等。
String str1 = "hello";
String str2 = new String("hello");
String str3 = "hello";
// 使用 == 比较字符串的引用相等
System.out.println(str1 == str2); // false -- 一个在常量池中,一个在堆内存中
System.out.println(str1 == str3); // true -- 都在常量池中
// 使用 equals 方法比较字符串的相等
System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // true
构造方法有哪些特点?是否可被 override?
构造方法特点如下:
- 名字与类名相同。
- 没有返回值,但不能用 void 声明构造函数。
- 生成类的对象时自动执行,无需调用。
public class Person {
private String name;
private int age;
// 构造方法没有返回值,并且不能用void声明构造函数
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void display() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
public static void main(String[] args) {
// 生成对象时自动执行构造方法
Person person = new Person("John", 25);
person.display();
}
}
构造方法不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况
面向对象三大特征
面向对象编程的三大特征是:封装、继承和多态。
-
封装(Encapsulation):封装是将对象的属性和方法组合在一起,形成一个独立的、自包含的整体,对外部隐藏对象的内部细节,只向外部提供公共接口(方法),以便外部的代码能够通过这些接口访问对象。封装可以提高代码的可维护性和重用性,同时也可以保护对象的数据安全。
// 例如 封装一个实体类 public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } }
-
继承(Inheritance):继承是指在已有类的基础上,创建一个新的类,并且让新类拥有已有类的属性和方法。被继承的类称为父类或基类,继承的类称为子类或派生类。子类可以在不重复编写代码的情况下,拥有父类的公共属性和方法,同时还可以增加自己的属性和方法,以实现更加复杂的功能。
// 继承上面那个父类,下面这个是子类 public class Student extends Person { private int grade; public Student(String name, int age, int grade) { super(name, age); this.grade = grade; } public int getGrade() { return grade; } public void setGrade(int grade) { this.grade = grade; } }
-
多态(Polymorphism):多态是指同一个方法调用,由于对象不同可能会产生不同的行为。多态可以让不同的对象对同一个消息做出不同的响应,从而提高代码的灵活性和可扩展性。多态有两种实现方式:静态多态(重载)和动态多态(重写和接口实现)。
-
在 Java 中,多态的实现主要依赖于两个机制:继承和方法重写。
-
调用子类中重写的
move()
方法,从而产生不同的行为。这就是多态的体现。
// 多态示例代码 public class Animal { public void move() { System.out.println("Animal move"); } } public class Dog extends Animal { public void move() { System.out.println("Dog move"); } } public class Cat extends Animal { public void move() { System.out.println("Cat move"); } } public class Main { public static void main(String[] args) { Animal a1 = new Animal(); Animal a2 = new Dog(); // 使用子类对象替换父类对象 Animal a3 = new Cat(); // 使用子类对象替换父类对象 a1.move(); a2.move(); a3.move(); } }
-
注意,三个对象指定的都是 父类(Animal)
关于继承如下 3 点需要记住:
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
子类无法直接访问父类的私有属性和方法
class ParentClass {
private String privateProperty = "Private property";
public void publicMethod() {
System.out.println("Public method");
}
private void privateMethod() {
System.out.println("Private method");
}
}
class ChildClass extends ParentClass {
public void childMethod() {
System.out.println("Child method");
System.out.println(publicProperty); // 子类可以访问父类的公有属性
publicMethod(); // 子类可以调用父类的公有方法
// 子类无法访问父类的私有属性和私有方法
// System.out.println(privateProperty); // 这行代码会报错
// privateMethod(); // 这行代码会报错
}
}
public class Main {
public static void main(String[] args) {
// 创建子类对象
ChildClass child = new ChildClass();
// 子类对象可以访问和调用父类的公有属性和方法
System.out.println(child.publicProperty);
child.publicMethod();
}
}
子类可以间接访问父类的私有成员 – 所以说拥有
class ParentClass {
private String privateProperty = "Private property";
private void privateMethod() {
System.out.println("Private method");
}
public void accessPrivateMembers() {
System.out.println(privateProperty); // 子类间接访问父类的私有属性
privateMethod(); // 子类间接调用父类的私有方法
}
}
class ChildClass extends ParentClass {
// 子类继承了父类的私有属性和私有方法,但无法直接访问
public void accessParentPrivateMembers() {
accessPrivateMembers(); // 子类间接访问父类的私有成员
}
}
public class Main {
public static void main(String[] args) {
ChildClass child = new ChildClass();
child.accessParentPrivateMembers();
}
}
多态的特点:
- 对象类型(父类)和引用类型(子类)之间具有继承(类)/实现(接口)的关系;
- 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
- 多态不能调用“只在子类存在但在父类不存在”的方法;(多态指向的都是父类)
- 如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法。
接口和抽象类有什么共同点和区别?
interface
和 abstract
共同点:
- 都不能被实例化。
- 都可以包含抽象方法。
- 都可以有默认实现的方法(Java 8 可以用
default
关键字在接口中定义默认方法)。
区别:
- 【接口】主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。【抽象类】主要用于代码复用,强调的是所属关系。
- 一个类只能继承一个类,但是可以实现多个接口。
- 【接口】中的成员变量只能是
public static final
类型的,不能被修改且必须有初始值,而【抽象类】的成员变量默认 default,可在子类中被重新定义,也可被重新赋值。
// 定义一个接口
public interface Animal {
public void eat();
}
// 定义一个抽象类
public abstract class Vehicle {
private int speed;
public void setSpeed(int speed) {
this.speed = speed;
}
public int getSpeed() {
return speed;
}
public abstract void run(); // 抽象方法
}
// 实现接口
public class Dog implements Animal {
public void eat() {
System.out.println("The dog is eating.");
}
}
// 继承抽象类
public class Car extends Vehicle {
public void run() {
System.out.println("The car is running.");
}
}
// 测试类
public class Test {
public static void main(String[] args) {
// 使用接口
Animal animal = new Dog();
animal.eat(); // The dog is eating.
// 使用抽象类
Vehicle vehicle = new Car();
vehicle.setSpeed(60);
System.out.println("The speed of the car is " + vehicle.getSpeed() + " km/h."); // The speed of the car is 60 km/h.
vehicle.run(); // The car is running.
}
}
深拷贝和浅拷贝的区别了解吗?什么是引用拷贝?
【深拷贝】会完全复制整个对象,会在堆上新创建一个地址(原对象的内部对象 Address
);
而【浅拷贝】只会在堆上新创建一个对象,但是内部地址还是原对象的,与原对象共用一个 Address
对象。
【引用拷贝】不会创建一个新的对象,而只是单纯的引用堆里面的原对象。
System.out.println("The speed of the car is " + vehicle.getSpeed() + " km/h."); // The speed of the car is 60 km/h.
vehicle.run(); // The car is running.
}
}
深拷贝和浅拷贝的区别了解吗?什么是引用拷贝?
【深拷贝】会完全复制整个对象,会在堆上新创建一个地址(原对象的内部对象 Address
);
而【浅拷贝】只会在堆上新创建一个对象,但是内部地址还是原对象的,与原对象共用一个 Address
对象。
【引用拷贝】不会创建一个新的对象,而只是单纯的引用堆里面的原对象。
学习参考
Java基础常见面试题总结(中) | JavaGuide(Java面试 + 学习指南)