Java 学习笔记(基础篇12)
1. static 静态变量:
① 被 static 修饰的成员变量,成为静态变量
- 特点:该类被所有类共享、不属于对象,属于类、随着类的加载而加载,优于对象存在
- 调用方法:类名调用
② 被 static 修饰的成员方法,成为静态方法
- 特点:多用在测试类和工具类中、JavaBean类中很少用
- 调用方法:类名调用
③ 注意
(1) 静态方法只能访问静态变量和静态方法
(2) 非静态方法可以访问所有(静态\非静态的(成员)变量\方法)
(3) 静态方法是没有this关键字
④ 总结:
(1) 静态方法中,只能访问静态
(2) 非静态方法可以访问所有
(3) 静态方法无this关键字
2. main 方法重识
3. 继承
面向对象三大特性:封装、继承、多态。
① 继承:
- java提供的一个关键字extends,可以让一个类和另一个类建立起继承关系。public class Student extends Person{}
- 子类可以在父类的基础上添加新功能,继承父类方法和变量
当类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就可以考虑使用继承,来优化代码。子类只能访问父类中非私有的成员
② 注意事项:
(1) Java 是单继承的:一个类只能继承一个直接父类;Java 中的类不支持多继承,但是支持多层继承。
(2) Object 类是 Java 中所有类的祖宗。
class A {} //extends Object{}
class B extends A{}
// class C extends B , A{} // 报错
class D extends B{}
③ 继承中成员的可继承性分析
(1) 成员变量
修饰符 | 能否继承? | 访问权限 |
---|---|---|
非私有(public /protected / 默认) | ✅ 直接继承 | 子类可直接访问(默认修饰符仅同包子类可直接访问) |
private | ✅ 继承(占子类对象内存),但 ❌ 直接访问 | 需通过父类的 getter/setter 间接操作 |
(2) 成员方法
修饰符 | 能否继承? | 说明 |
---|---|---|
非私有(public /protected / 默认) | ✅ 直接继承 | 子类可重写或直接调用 |
private | ❌ 不继承 | 父类私有方法对 subclass 完全隐藏 |
(3) 构造方法
- 任何构造方法都不能被继承!
- 子类构造时,必须通过
super()
调用父类构造(隐式或显式),但父类构造方法本身不会成为子类的方法。
(4) PS:构造方法和成员方法的区别:
构造方法:创建并初始化对象(给对象的成员变量赋初始值)。
成员方法:定义对象的行为 / 功能(如计算、打印、逻辑处理等)。
④ 继承中成员变量的访问特点
(1) 就近原则:访问成员变量时,优先使用 “距离近” 的变量(子类有则用子类的;子类没有,再找父类)。
class Father { int num = 10; }
class Son extends Father { int num = 20; }
Son s = new Son();
System.out.println(s.num); // 输出 20(优先用子类的 num)
(2) super
调用:若需明确访问父类的成员变量,用 super.变量名
,直接到父类中查找。
class Son extends Father {public void show() {System.out.println(super.num); // 输出 10(访问父类的 num)}
}
⑤ 方法重写(Override)
(1) 适用场景:当父类的方法无法满足子类当前的功能需求时,子类对父类方法进行重写。
(2) 语法格式:子类与父类有完全一致的方法声明(方法名、参数列表、返回值类型相同),子类的该方法即为 “重写的方法”。
(3) @Override
注解的作用:加在子类重写的方法上,校验重写语法是否正确(如方法签名不匹配,IDE 会显示红色波浪线报错)。
(4) 底层原理:子类重写方法后,JVM 的虚方法表中,该方法的 “父类版本” 会被 “子类版本” 覆盖。
⑥ 继承中成员方法的重写规则
(1) 签名一致性:重写方法的名称、参数列表(行式) 必须与父类完全一致。
(2) 访问权限更宽松:子类重写方法的访问权限 ≥ 父类方法(如父类是 protected
,子类可改为 public
)。
(3) 返回值类型更严格 / 兼容:子类重写方法的返回值类型 ≤ 父类方法(支持 “协变返回”,例如父类返回 Number
,子类可返回 Integer
)。
(4) 建议性原则:重写方法尽量与父类保持一致(保证子类行为对父类的 “兼容性”)。
(5) 底层限制:只有被加入 虚方法表 的方法(非 private
、非 static
、非 final
的方法),才能被重写。
⑦ 继承中「构造方法」的访问特点
(1) 不可继承,但可调用:子类不能直接继承父类的构造方法,但可以通过 super(...)
调用父类构造。
(2) 默认调用父类无参构造:子类构造方法的第一行,默认隐含 super()
(自动调用父类的无参构造)。
(3) 执行顺序:子类所有构造方法,会先调用父类的无参构造,再执行自身的构造逻辑。
(4) 调用父类有参构造:若需调用父类的有参构造,必须手动书写 super(参数)
(替换默认的 super()
)。
4. 多态
① 多态是继承 / 实现关系下的现象,表现为:对象多态(父类引用可指向子类对象)、行为多态(同一方法调用,子类有不同实现)。
② 具体代码体现:父类引用指向子类对象,调用方法时执行子类重写的逻辑:
People p1 = new Student(); // 父类People引用指向子类Student对象
p1.run(); // 执行Student类中重写的run()方法People p2 = new Teacher();
p2.run(); // 执行Teacher类中重写的run()方法
③ 多态的前提
- 存在继承 / 实现关系(类继承父类,或类实现接口);
- 存在父类引用指向子类对象(如
父类 变量 = new 子类()
); - 存在方法重写(子类重写父类的方法)。
④ 调用规则:
(1) 方法编译看左边,运行看右边
- 编译阶段(
javac
):检查左边父类引用的类型是否包含要调用的方法。若父类有该方法,编译通过;若没有,直接报错。 - 运行阶段(
java
):实际执行右边子类对象中重写后的方法(若子类重写了父类方法)。
Animal a = new Dog();
a.run(); // 编译时:检查Animal类是否有run()方法;运行时:执行Dog类重写的run()方法
(2) 成员变量访问规则:由左边的引用类型决定
- 子类对象会继承父类的成员变量,但通过 “父类引用” 访问时,使用的是父类的成员变量(成员变量不体现 “多态”,多态是方法的特性)。
⑤ 多态的优势
(1) 解耦合,便于扩展和维护:多态场景下(如 父类 引用 = new 子类();
),“右侧实际创建的子类对象” 可灵活替换,上层调用代码无需修改。新增子类或修改子类逻辑时,不影响原有代码结构,降低依赖、方便维护。
(2) 方法参数的通用性:当方法的参数声明为父类类型时,该方法可以接收这个父类的所有子类对象。无需为每个子类单独编写方法,提升代码复用性。
⑥ 多态的弊端
(1) 多态的核心是 “父类引用指向子类对象”,但此时 父类引用无法直接调用子类的 “特有方法”(即子类独有的、父类未定义的方法)。
class Animal { public void eat() {} }
class Dog extends Animal {public void eat() { System.out.println("吃骨头"); }public void watchHome() {} // Dog的特有方法
}Animal a = new Dog();
a.eat(); // 合法(父类、子类都有该方法)
a.watchHome(); // 编译错误!父类Animal没有该方法,无法调用
(2) 弊端的解决方案:向下转型(结合 instanceof
判断)
- Java 新特性:模式匹配(
instanceof
直接结合强转)
Animal a = new Dog();
if (a instanceof Dog d) { // 若a是Dog类型,直接强转为变量dd.watchHome(); // 用d调用特有方法
} else {// 非Dog类型的逻辑
}
代码示例:
package Test2;
public class test {public static void main(String[] args) {Person p1 = new Person("老王", 30);Dog d = new Dog("黑色", 2);Cat c = new Cat("橘黄色", 3);p1.keepPet(d, "狗粮"); // keepPet参数若为“父类(如Animal)”,可接收Dog子类对象// 若需调用Dog特有方法(如watchHome),则需向下转型:if (d instanceof Dog dog) {dog.watchHome();}}
}
⑦ 综合练习
题目:宠物喂养管理系统
需求:
设计一个简单的宠物喂养管理系统,通过面向对象的继承、多态等特性实现不同宠物的行为表现,具体包含以下类结构:
(1) 父类 Animal
- 包含私有属性:
age
(年龄,int
类型)、color
(颜色,String
类型)。 - 提供构造方法:无参构造和带
age
、color
参数的有参构造。 - 提供公共方法:
getAge()
(获取年龄)、getColor()
(获取颜色);以及抽象方法eat(String something)
(需子类实现,定义宠物 “吃食物” 的行为)。
(2) 子类 Dog
- 继承自
Animal
,提供无参构造和带age
、color
参数的有参构造(需调用父类构造)。 - 重写
eat(String something)
方法:输出格式为 “X 岁的 Y 颜色的狗两只前腿死死的抱住 Z 猛吃”(其中 X 是年龄,Y 是颜色,Z 是传入的食物)。 - 新增特有方法
lookHome()
:输出 “狗在看家”。
(3) 子类 Cat
- 继承自
Animal
,提供无参构造和带age
、color
参数的有参构造(需调用父类构造)。 - 重写
eat(String something)
方法:输出格式为 “X 岁的 Y 颜色的猫眯着眼睛侧着头吃 Z”(参数含义同上)。 - 新增特有方法
catchMouse()
:输出 “猫抓老鼠”。
(4) 人类 Person
- 包含私有属性:
name
(姓名,String
类型)、age
(年龄,int
类型)。 - 提供构造方法:无参构造和带
name
、age
参数的有参构造。 - 定义方法
keepPet(Animal a, String something)
:- 通过
instanceof
判断传入的Animal
是Dog
还是Cat
。 - 若为
Dog
:输出 “年龄为 A 岁的 B 养了一只 C 颜色的 D 岁的狗”(A 是Person
的年龄,B 是姓名,C 是狗的颜色,D 是狗的年龄);然后调用狗的eat(something)
和lookHome()
方法。 - 若为
Cat
:输出 “年龄为 A 岁的 B 养了一只 C 颜色的 D 岁的猫”(参数含义同上);然后调用猫的eat(something)
和catchMouse()
方法。 - 若既不是
Dog
也不是Cat
,输出 “没有这种动物”。
- 通过
(5) 测试类 Test
在main
方法中:
- 实例化一个
Person
对象(姓名 “老王”,年龄 30)。 - 实例化一个
Dog
对象(年龄 2,颜色 “黑”),调用Person
的keepPet
方法,给狗喂 “骨头”。 - 实例化一个
Cat
对象(年龄 3,颜色 “灰”),调用Person
的keepPet
方法,给猫喂 “鱼”。
package Test3;public class test {public static void main(String[] args) {Person p = new Person("老王",30);Dog d = new Dog(2,"黑");Cat c = new Cat(3,"灰");p.keepPet(d,"骨头");p.keepPet(c,"鱼");}
}
关键逻辑:
@Overridepublic void eat(String something) {System.out.println(getAge() + "岁的" + getColor()
+ "颜色的狗两只前腿死死的抱住" + something + "猛吃"); //不太理解}
getAge()
、getColor()
前面不需要加.
,核心原因是:这些方法是当前对象(this
)自身的方法,在实例方法中调用自身方法时,默认会隐式使用this
关键字,而this.
可以省略不写。getAge()
和getColor()
是从父类Animal
继承的方法(通常是属性的 getter 方法,用于获取age
和color
的值)。由于Dog
继承了Animal
,这些方法就成为了Dog
类自身的方法,属于Dog
实例(对象)。
package Test3;public class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public void keepPet(Dog dog, String something) {System.out.println("年龄为" + age + "岁的" + name + "养了一只" + dog.getColor() + "颜色的" + dog.getAge() + "岁的狗"); //为什么这里不用getAge这些}
}
- 在
Person
类的keepPet
方法中,直接使用age
和name
而不通过getAge()
、getName()
方法,核心原因是:这些变量是当前类(Person
类)自己的私有成员变量,在本类内部可以直接访问,无需通过 getter 方法。 Dog
类虽然继承了Animal
类,但作为子类,它无法直接访问父类Animal
中被private
修饰的age
和color
。因此,要获取这两个属性的值,必须依赖父类Animal
提供的public
级别的 getter 方法(getAge()
和getColor()
)。