继承
在现实世界中,狗和猫都是动物,这是因为他们都有动物的一些共有的特征。
在Java中,可以通过继承的方式来让对象拥有相同的属性,并且可以简化很多代码
例如:动物都有的特征,有名字,有年龄,那么让猫和狗都继承于动物,这样猫和狗就也有了名字和年龄。
需要使用的关键字:extends
class Animals{
int age;
String name;
}
class Dogs extends Animals{
String height;
}
class Cats extends Animals{
String weight;
}
通过继承的方式就能让dog和cat继承animals的age和name属性。并且不止属性,父类中的方法也是可以继承的。
但是要是父类和子类中的属性名或者方法名因为某些原因重名了呢?
父类成员变量、成员方法访问
如果属性名相同,那么则会优先访问自己的,而不是父类的:
public class Base {
int a;
int b;
int c;
}
public class Derived extends Base{
int a;
char b;
public void method(){
a = 100;
b = 101;
c = 102;
}
}
成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
例如上面的会优先访问class Derived中有的,如果没有再访问到Base中的。
方法同理,也是优先访问子类的再访问父类的;但是方法有个特殊的点:
如果子类父类方法是重名的但是形成重载了,也就是参数列表不同,那么会优先匹配重载的那个方法。
super关键字
如果父类和子类是重名的,但是想要访问父类的方法,这个时候就可以使用super关键字,使得拿到的对象/方法直接就是父类的。
class Animal {
void eat() {
System.out.println("这里是父类");
}
}
class dog extends Animal {
void eat() {
super.eat();
System.out.println("这里是子类");
}
}
这样就会先拿到父类中的eat方法,然后再打印子类中的相关语句。
super同样可以适用成员变量:
class Animal {
String name = "Animal";
}
class Dog extends Animal {
String name = "Dog";
void printName() {
System.out.println(super.name);
System.out.println(this.name);
}
}
super和this的区别
相同点:
- 都是Java中的关键字
- 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
- 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
不同点:
- this是当前对象的引用,而super相当于是子类对象中从父类继承下来部分成员的引用
- 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
- 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现
- 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有
初始化顺序
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("构造方法执行");
}
{
System.out.println("实例代码块执行");
}
static {
System.out.println("静态代码块执行");
}
}
在上面这个代码中,执行的顺序是先执行静态的,再执行实例的,再执行构造方法;那在有继承关系的代码块中是什么样子呢?
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person:构造方法执行");
}
{
System.out.println("Person:实例代码块执行");
}
static {
System.out.println("Person:静态代码块执行");
}
}
class Student extends Person{
public Student(String name,int age) {
super(name,age);
System.out.println("Student:构造方法执行");
}
{
}
System.out.println("Student:实例代码块执行");
static {
System.out.println("Student:静态代码块执行");
}
}
上面的代码执行结果如下:
- Person: 静态代码块执行
- Student:静态代码块执行
- Person: 实例代码块执行
- Person: 构造方法执行
- Student:实例代码块执行
- Student:构造方法执行
先是父类和子类静态方法执行,然后再是父类的实例和构造方法,再试子类的示例和构造方法。
final关键字
final关键可以用来修饰变量、成员方法以及类。
修饰变量
修饰变量或字段,表示常量(即不能修改)。
inal int a = 10;
a = 20; // 编译出错
修饰类
表示此类不能被继承。
inal public class Animal {
...
}
public class Bird extends Animal {
...
}
编译出错。
修饰方法
表示该方法不能被重写
多态
简单地说,就是使用不同的对象去完成时会产生出不同的状态,所以叫做多态。
在java中要实现多态,必须要满足如下几个条件,缺一不可:
- 必须在继承体系下
- 子类必须要对父类中方法进行重写
- 通过父类的引用调用重写的方法
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
class People{
public void eat(){
System.out.println("吃饭");
}
}
class Boy extends People{
@Override
public void eat(){
System.out.println("男孩子吃饭");
}
}
class Girl extends People{
@Override
public void eat(){
System.out.println("女孩子吃饭");
}
}
public class demo12_Polymorphism2 {
public static void main(String[] args) {
People people = new People();
Boy boy = new Boy();
Girl girl = new Girl();
people.eat();
boy.eat();
girl.eat();
}
}
people.eat();
将调用People
类的eat
方法,并打印 "吃饭"。boy.eat();
将调用Boy
类重写的eat
方法,并打印 "男孩子吃饭"。girl.eat();
将调用Girl
类重写的eat
方法,并打印 "女孩子吃饭"。
重写和重载
重写:
class dog {
public void bark(){
System.out.println("wangwangwang");
}
}
class pipi extends dog {
@Override
public void bark(){
System.out.println("houhouhou");
}
}
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
父类被static、private修饰的方法、构造方法都不能被重写。
重载:
class dog {
public void bark(){
System.out.println("wangwangwang");
}
public void bark(int num){
System.out.println("houhouhou");
}
}
向上转型和向下转型
向上转型
Animal animal = new Cat("狗狗");
通过父类,新建一个子类的成员,这就是向上转型。
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
向下转型
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时将父类引用再还原为子类对象即可,即向下转换。
向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。
Java中为了提高向下转型的安全性,引入 了instanceof,如果该表达式为true,则可以安全转换。
public class DowncastingExample {
public static void main(String[] args) {
// 向上转型:创建 Cat 类型的实例并赋值给 Animal 类型的引用
Animal animal = new Cat("狗狗");
// 下面进行向下转型,尝试将 Animal 类型的引用转换为 Cat 类型
// 在转型之前,我们首先检查 animal 是否确实引用了 Cat 类的实例
if (animal instanceof Cat) {
// 安全进行向下转型
Cat myCat = (Cat) animal;
System.out.println("向下转型成功,猫的名字是:" + myCat.name);
} else {
System.out.println("引用的不是 Cat 类型的实例,转型失败。");
}
}
}
使用多态的好处
1. 能够降低代码的 "圈复杂度", 避免使用大量的 if - else
2. 可扩展能力更强
多态缺陷:代码的运行效率降低。