Java 面向对象核心知识点(类与对象篇)
前言:
面向对象的本质:以类的方式组织代码,以对象的方式(组织)封装数据
1. 类与对象创建
定义:类是一种抽象的数据类型,类是对象的模板,定义对象的(静态的)属性和(动态的)行为。
1.1 类定义与实例化
public class Person {
// 属性(字段)(成员变量)
String name;
int age;
// 方法
void speak() {
System.out.println("I'm " + name);
}
}
// 创建对象(类实例化后会返回一个自己类型的对象)
// p1就是Person类的一个实例
Person p1 = new Person(); // 使用 new 关键字创建对象,堆内存分配空间
p1.name = "Alice"; // 属性赋值
p1.speak(); // 方法调用
内存模型:
栈内存(p1) → 堆内存(Person实例)
├─ name: "Alice"
└─ age: 0
分析:栈(Stack)内存放的变量引用名,堆(Heap)内存放的是实际的对象属性值和方法。
Ps:多个引用变量可以指向同一个对象。
示例:Person p2 = p1; // p2 和 p1 指向同一对象
1.2 构造器详解
public class Student {
String id;
// 默认构造器(无参)
public Student() {
this.id = "0000"; // this指代当前对象,实例化初始值
}
// 带参构造器(重载),构造器支撑重载
public Student(String id) {
this(); // 调用无参构造器(必须首行)
this.id = id;
}
}
定义: 构造器用于创建对象时 初始化,与类同名,无返回值
关键规则:
- 构造器名必须与类名相同
- 没有返回值类型(连void都没有)
- 默认提供无参构造器(仅当没有自定义构造器时),而当存在有参构造器时必须显式定义无参构造器
this()
必须放在构造器首行- 在main方法中使用new 实例化对象,本质就是在调用构造器
public class Person {
String name;
int age;
public Person() {
this("Unknown", 0); // 调用另一个构造器
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
分析:
- 构造器内部可调用
this()
,但必须放在第一行 - 构造器可用于初始化默认值,减少 setter 方法调用
2. 封装机制(Encapsulation)
定义: 封装 是面向对象的基本原则,目的是保护数据,防止外部直接访问。
2.1 访问控制修饰符
修饰符 | 类内 | 同包 | 子类 | 任意位置 |
---|---|---|---|---|
private | ✓ | ✗ | ✗ | ✗ |
default | ✓ | ✓ | ✗ | ✗ |
protected | ✓ | ✓ | ✓ | ✗ |
public | ✓ | ✓ | ✓ | ✓ |
2.2 标准JavaBean
public class User {
//使用 private 限制访问,确保属性(字段)只能在类内访问,也可以避免被直接修改
private final String id; //final 修饰不变对象,让变量不可变,提升线程安全
private String username;
private String password;
// 提供公共方法
// 使用 getter 和 setter 方法,初始化/获取变量方法
public String getUsername() {
return username;
}
public void setUsername(String name) {
this.username = name;
}
// 其他方法...
}
封装优势:
- 数据保护(防止非法赋值)
- 隐藏实现细节
- 便于修改维护
3. 继承(Inheritance)与super
定义: 继承使得子类复用父类的属性和方法,使用 extends 关键字。
3.1 继承基础
//父类
class Animal {
String name;
public Animal(){
System.out.println("Aniaml 无参构造执行");
}
void eat() {
System.out.println("Eating...");
}
}
//子类(派生类)Dog,继承父类(基类)的name属性和eat方法
class Dog extends Animal { // 单继承
public Dog(){
//隐藏代码:调用了父类的无参构造
super();
Sywtem.out.println("Dog 无参构造执行");
}
void bark() {
System.out.println("Woof!");
}
}
public class InheritanceDemo {
public static void main(String[] args){
Dog dog = new Dog(); //自动调用构造器
dog.name = "Buddy";
dog.eat(); // 继承自 Animal
dog.bark(); // Dog 自己的方法
}
}
输出:
Aniaml 无参构造执行
Dog 无参构造执行
Eating…
Woof!
3.2 super关键用法
- 调用父类构造方法
public class Animal {
String name;
public Animal(String name) {
this.name = name;
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name); // 调用父类构造方法
}
}
-调用父类方法
class Parent {
int value = 10;
void show() {
System.out.println("Parent");
}
}
class Child extends Parent {
int value = 20;
@Override
void show() {
System.out.println(this.value); //访问该对象字段(20)
System.out.println(super.value); // 访问父类字段(10)
super.show(); // 调用父类方法
}
}
继承限制:
- Java不支持多继承(但支持多接口实现)
- 父类private成员不可直接访问
- final类 不能被继承
super规则:
- 子类构造器中调用父类构造器
super()
必须是第一行。(隐藏默认调用父类无参构造器) - super必须只能出现在子类的方法或构造方法中。
- super和this不能同时调用构造方法。
- 子类尽量使用 super 复用父类逻辑,避免代码重复。
4. 方法重写(Override)
4.1 重写规则
重写要求:
- 方法名和参数列表完全相同
- 返回值类型相同或是子类(协变返回)
- 不能缩小访问权限(public -> protected ❌)(访问权限不能比父类更严格)
- 必须有 @Override 注解,防止拼写错误。
- 不能重写private/final/static方法
class Shape {
public void draw() {
System.out.println("Drawing shape");
}
}
class Circle extends Shape {
@Override // 注解
public void draw() {
System.out.println("Drawing circle");
}
//如果可以,优先使用 super.method() 复用父类逻辑,减少代码重复。
}
ps:
以上方法为非静态方法,若以父类引用指向子类
Shape s=new Circle();(子类的构造方法)
s.draw();
非静态方法调用的是对象的方法,输出Drawing circle。
但如果使用static修饰静态方法,静态方法是同类加载的,调用的是类的方法,输出Drawing shape
@Override作用:
- 编译器检查是否符合重写规则
- 提高代码可读性
5.super 与 this 的区别
5.1 super
作用:
- 用于访问父类的成员(字段、方法)。
- 在子类构造器中调用父类构造器,确保父类部分正确初始化。
使用场景:
- 访问父类方法:在子类重写方法中,调用父类原有方法的实现。
- 访问父类字段:在子类中区分父类字段与子类字段(当名称相同时)。
- 调用父类构造器:在子类构造器中,使用
super(...)
调用父类构造方法,必须位于构造器的第一行。
示例:
class Parent { public int value = 10; public Parent(String msg) { System.out.println("Parent constructor: " + msg); } public void show() { System.out.println("Parent show"); } } class Child extends Parent { public int value = 20; public Child(String msg) { super(msg); // 调用父类构造器,必须是第一行 System.out.println("Child constructor: " + msg); } @Override public void show() { super.show(); // 调用父类方法 System.out.println("Child show"); } public void display() { System.out.println("Child value: " + this.value); System.out.println("Parent value: " + super.value); } } public class Test { public static void main(String[] args) { Child child = new Child("Hello"); child.show(); child.display(); } }
输出:
Parent constructor: Hello
Child constructor: Hello
Parent show
Child show
Child value: 20
Parent value: 10
5.2 this
作用:
- 引用当前对象的成员(字段、方法)。
- 在构造器中调用同一类中其他构造器,实现构造器重用。
使用场景:
- 区分成员变量和局部变量:当参数名称与成员变量名称相同时,使用
this
表示当前对象的成员变量。 - 调用本类其他构造器:在一个构造器中,通过
this(...)
调用同类中其他构造器,必须是构造器中的第一行。
- 区分成员变量和局部变量:当参数名称与成员变量名称相同时,使用
示例:
class Person { private String name; private int age; // 使用 this 调用其他构造器 public Person() { this("Unknown", 0); // 调用带参构造器 } public Person(String name, int age) { // 使用 this 区分成员变量与局部变量 this.name = name; this.age = age; } public void introduce() { System.out.println("My name is " + this.name + ", age " + this.age); } } public class Test { public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person("Alice", 25); p1.introduce(); p2.introduce(); } }
输出:
My name is Unknown, age 0
My name is Alice, age 25
5.3 总结对比
关键字 | 主要用途 | 使用位置 | 注意事项 |
---|---|---|---|
super | 访问父类成员,调用父类构造器 | 子类方法、构造器中 | 在构造器中,必须是第一行调用 |
this | 表示当前对象,访问本类成员;调用本类其他构造器 | 本类方法、构造器中 | 在构造器中,this() 调用也必须放在第一行 |
6.重载(Overload)与重写(Override)的区别
6.1 重载(Overload)
定义:
在同一个类中,方法名相同,但参数列表(参数个数或参数类型)不同。
重载体现的是 编译时多态性(静态绑定)。特点:
- 返回类型可以相同也可以不同。(
返回类型无法决定是否重载
) - 方法名必须完全相同;参数列表必须不同。
- 与访问修饰符无关。
- 返回类型可以相同也可以不同。(
示例:
public class Calculator { // 加法重载 public int add(int a, int b) { return a + b; } public double add(double a, double b) { return a + b; } // 参数个数不同 public int add(int a, int b, int c) { return a + b + c; } }
6.2 重写(Override)
定义:
子类重写父类中已经存在的方法(方法名、参数列表必须一致),以提供新的实现。
重写体现的是 运行时多态性(动态绑定)。特点:
- 必须保证方法名、参数列表完全一致。
- 返回类型可以为父类返回类型的子类(协变返回类型)。
- 访问权限不能比父类更严格(例如,父类方法是 public,子类不能重写为 protected 或 private)。
- 建议使用
@Override
注解,帮助编译器检测是否正确重写了父类方法。
示例:
class Animal { public void makeSound() { System.out.println("Animal sound"); } } class Dog extends Animal { @Override public void makeSound() { System.out.println("Bark"); } } public class Test { public static void main(String[] args) { Animal animal = new Dog(); // 编译时为 Animal,但运行时为 Dog animal.makeSound(); // 输出 "Bark",运行时调用子类的方法 } }
6.3 总结对比
特性 | 重载(Overload) | 重写(Override) |
---|---|---|
定义 | 同一类中方法名相同,参数列表不同 | 子类中方法名、参数列表与父类完全一致 |
多态性 | 编译时多态性(静态绑定) | 运行时多态性(动态绑定) |
返回类型 | 可相同也可不同 | 必须与父类方法相同或为其子类(协变返回类型) |
访问修饰符 | 与方法重载无直接关系 | 子类方法不能缩小父类方法的访问权限 |
注解 | 无特定要求 | 建议使用 @Override 注解 |
⚡ 高频面试题
创建对象时的内存分配过程?
this和super能否同时出现?
以下代码输出什么?
class A { int i = 10; void print() { System.out.println(i); } } class B extends A { int i = 20; public static void main(String[] args) { new B().print(); } }
上文链接:https://blog.csdn.net/weixin_73492487/article/details/146284259