JAVA-面向对象进阶
static(静态)
- 关于
static
关键字的使用,它可以用来修饰的成员变量和成员方法 - 被static修饰的成员是属于类的是放在静态区中
- 没有static修饰的成员变量和方法则是属于对象的。
静态变量及其访问
有static
修饰成员变量,说明这个成员变量是属于类的,这个成员变量称为类变量或者静态成员变量。 直接用类名访问即可,所有的对象都可以共享这个方法。
因为类只有一个,所以静态成员变量在内存区域中也只存在一份。所有的对象都可以共享这个变量。
实例变量及其访问
无static
修饰的成员变量属于每个对象的, 这个成员变量叫实例变量,之前我们写成员变量就是实例成员变量。
需要注意的是:实例成员变量属于每个对象,必须创建类的对象才可以访问。
区分:
类.类变量(类.静态成员变量) | 对象.实例成员变量 |
---|
继承
-
继承描述的是事物之间的所属关系
-
父类更通用,子类更具体
-
继承:就是子类继承父类的属性和行为,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
父类中的私有成员变量,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用
private
修饰成员变量,通过父类中提供公共的getXxx方法和setXxx方法进行访问。
继承的好处
- 提高代码的复用性(减少代码冗余,相同代码重复利用)。
- 使类与类之间产生了关系。
定义格式 extends
class 父类 {
...
}
class 子类 extends 父类 {
...
}
注意:Java是单继承的,一个类只能继承一个直接父类
super访问父类成员变量
子父类中出现了同名的成员变量时,子类会优先访问自己对象中的成员变量。
- 子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用
super
关键字,修饰父类成员变量
注意:super代表的是父类对象的引用,this代表的是当前对象的引用。
class Fu {
// Fu中的成员变量。
int num = 5;
}
class Zi extends Fu {
// Zi中的成员变量
int num = 6;
public void show() {
int num = 1;
// 访问方法中的num
System.out.println("method num=" + num);
// 访问子类中的num
System.out.println("Zi num=" + this.num);
// 访问父类中的num
System.out.println("Fu num=" + super.num);
}
}
class Demo {
public static void main(String[] args) {
// 创建子类对象
Zi z1 = new Zi();
// 调用子类中的show方法
z1.show();
}
}
演示结果:
method num=1
Zi num=6
Fu num=5
-
方法重名
如果子类父类中出现重名的成员方法,则创建子类对象调用该方法的时候,子类对象会优先调用自己的方法。 -
方法重写
子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现 -
重写使用
子类继承了父类的方法,但是子类觉得父类的这方法不足以满足自己的需求,子类重新写了一个与父类同名的方法,以便覆盖父类的该方法
@Override重写注解
-
@Override:注解,重写注解校验!
-
这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。
-
建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!
加上后的子类代码形式如下:
public class Cat extends Animal {
// 声明不变,重新实现
// 方法名称与父类全部一样,只是方法体中的功能重写写了!
@Override
public void cry(){
System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!");
}
}
- 注意事项
- 方法重写是发生在子父类之间的关系。
- 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
- 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
- 子类构造方法执行的时候,都会在第一行默认先调用父类无参数构造方法一次。
- 子类构造方法的第一行都隐含了一个super()去调用父类无参数构造方法,super()可以省略不写。
super和this的用法格式
访问成员:
this.成员变量 -- 本类的
super.成员变量 -- 父类的
this.成员方法名() -- 本类的
super.成员方法名() -- 父类的
调用构造方法格式:
super(...) -- 调用父类的构造方法,根据参数匹配确认
this(...) -- 调用本类的其他构造方法,根据参数匹配确认
注意:
-
子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
-
super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
-
super(..)和this(...)是根据参数去确定调用父类哪个构造方法的。
-
super(..)可以调用父类构造方法初始化继承自父类的成员变量的数据。
-
this(..)可以调用本类中的其他构造方法。
public class Animal {
//姓名,年龄,颜色
private String name;
private int age;
private String color;
public Animal() {
}
public Animal(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
public class Cat extends Animal{
//因为猫类中没有独有的属性。
//所以此时不需要写私有的成员变量
//空参
public Cat() {
}
//需要带子类和父类中所有的属性
public Cat(String name, int age, String color) {
super(name,age,color);
}
}
public class Dog extends Animal{
//Dog :吼叫
private String wang;
//构造
public Dog() {
}
//带参构造:带子类加父类所有的属性
public Dog(String name, int age, String color,String wang) {
//共性的属性交给父类赋值
super(name,age,color);
//独有的属性自己赋值
this.wang = wang;
}
public String getWang() {
return wang;
}
public void setWang(String wang) {
this.wang = wang;
}
}
public class Demo {
public static void main(String[] args) {
//Animal : 姓名,年龄,颜色
//Cat :
//Dog :吼叫
//创建狗的对象
Dog d = new Dog("旺财",2,"黑色","嗷呜~~");
System.out.println(d.getName()+", " + d.getAge() + ", " + d.getColor() + ", " + d.getWang());
//创建猫的对象
Cat c = new Cat("中华田园猫",3,"黄色");
System.out.println(c.getName() + ", " + c.getAge() + ", " + c.getColor());
}
}
继承的特点
- Java只支持单继承,不支持多继承。
一个类只能有一个父类,不可以有多个父类。
-
一个类可以有多个子类。
-
可以多层继承。
多态
多态: 是指同一行为,具有多个不同表现形式。
多态体现的格式:
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
多态的前提:有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。
多态的应用示例:
父类:
public class Person {
private String name;
private int age;
空参构造
带全部参数的构造
get和set方法
public void show(){
System.out.println(name + ", " + age);
}
}
子类1:
public class Administrator extends Person {
@Override
public void show() {
System.out.println("管理员的信息为:" + getName() + ", " + getAge());
}
}
子类2:
public class Student extends Person{
@Override
public void show() {
System.out.println("学生的信息为:" + getName() + ", " + getAge());
}
}
子类3:
public class Teacher extends Person{
@Override
public void show() {
System.out.println("老师的信息为:" + getName() + ", " + getAge());
}
}
测试类:
public class Test {
public static void main(String[] args) {
//创建三个对象,并调用register方法
Student s = new Student();
s.setName("张三");
s.setAge(18);
Teacher t = new Teacher();
t.setName("王建国");
t.setAge(30);
Administrator admin = new Administrator();
admin.setName("管理员");
admin.setAge(35);
register(s);
register(t);
register(admin);
}
//这个方法既能接收老师,又能接收学生,还能接收管理员
//只能把参数写成这三个类型的父类
public static void register(Person p){
p.show();
}
}
多态的运行特点
调用成员变量时:编译看左边,运行看左边
调用成员方法时:编译看左边,运行看右边
代码示例:
Fu f = new Zi();
//编译看左边的父类中有没有name这个属性,没有就报错
//在实际运行的时候,把父类name属性的值打印出来
System.out.println(f.name);
//编译看左边的父类中有没有show这个方法,没有就报错
//在实际运行的时候,运行的是子类中的show方法
f.show();
多态编译阶段是看左边父类类型的,如果子类有独有的功能,此时多态的写法就无法访问子类独有功能,所以多态的弊端是:
- 多态无法访问子类独有功能
多态的前提
-
有继承或者实现关系
-
方法的重写【意义体现:不重写,无意义】@Override
-
父类引用指向子类对象【格式体现】
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
引用类型转换(转型)
多态无法访问子类独有功能
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
回顾基本数据类型转换
- 自动转换: 范围小的赋值给范围大的.自动完成:double d = 5;
- 强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14
多态的转型分为向上转型(自动转换)与向下转型(强制转换)两种。
向上转型(自动转换)
- 向上转型:多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。
当父类引用指向一个子类对象时,便是向上转型。
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。所以子类范围小可以直接自动转型给父类类型的变量。
向下转型(强制转换)
- 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
Cat c =(Cat) a;
案例演示
定义类:
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void watchHouse() {
System.out.println("看家");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
}
}
转型的异常
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了 ClassCastException
,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。
instanceof关键字
为了避免ClassCastException
的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。
所以,转换前,我们最好先做一个判断,代码如下:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
instanceof新特性
JDK14的时候提出了新特性,把判断和强转合并成了一行
//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具