目录
1.Java中,类只支持单继承,也就是说一个类只能有一个直接父类
5.java中被 final 标志的类,是最终类,不可以被继承
类的继承
1.类的继承的概念
Java中,类的继承是指在一个现有类的基础上去构建一个新的类,子类会自动拥有父类所有可继承的属性和方法
在程序中,如果想要声明一个类继承另外一个类,需要使用extends关键字,其基本语法如下
【修饰符】 class 子类名 extends 父类名{
}
class Dog{
public String name;
public int age;
public void eat(){
System.out.println("dog can eat");
}
}
class Bird {
public String name;
public int age;
public void eat() {
System.out.println("bird can eat");
}
public void fly() {
System.out.println("bird can fly");
}
}
在这个代码中,我们发现两个类有公共部分,那么我们可以将这些公共部分抽出来
class Animal {
public String name;
public int age;
public void eat() {
System.out.println("dog can eat");
}
}
class Dog extends Animal{
}
class Bird extends Animal{
public void fly() {
System.out.println("bird can fly");
}
}
public class text {
public static void main(String[] args) {
Dog dog=new Dog();
dog.name="汪汪";
dog.age=10;
System.out.println(dog.name);
System.out.println(dog.age);
}
}
Dog 和Bird称为子类或者派生类,Animal称为父类或者基类或者超类,Dog和Bird通过extends关键字继承了Animal类,子类会自动拥有父类所有公共的成员
2.注意事项
1.Java中,类只支持单继承,也就是说一个类只能有一个直接父类
c这个类不能同时继承Dog类和Bird类
2.子类只能继承父类的共有成员
class Animal {
public String name;
public int age;
public void eat() {
System.out.println("dog can eat");
}
private void fun(){
System.out.println("hi");
}
}
class Dog extends Animal{
}
父类Animal中,fun方法是私有方法,不能被子类继承
3.多个类可以继承同一个父类
class Dog extends Animal{
}
class Bird extends Animal{
public void fly() {
System.out.println("bird can fly");
}
}
4.Java可以满足多层继承
class Animal {
public String name;
public int age;
public void eat() {
System.out.println("dog can eat");
}
public void fun(){
System.out.println("hi");
}
}
class Bird extends Animal{
public void fly() {
System.out.println("bird can fly");
}
}
class Dog extends Bird{
}
public class text {
public static void main(String[] args) {
Dog dog=new Dog();
dog.age=10;
dog.name="汪汪";
dog.fly();
dog.fun();
}
}
Bird类继承了Animal类,Dog类继承了Bird类,那么Dog类可以拥有Bird类和Animal类的所有公开成员
5.java中被 final 标志的类,是最终类,不可以被继承
6.继承的执行顺序
一个类,先执行静态代码块,后执行实例化代码块,后执行构造方法
class A{
static {
System.out.println("父类的静态代码块");
}
{
System.out.println("父类的实例化代码块");
}
public A(){
System.out.println("父类的构造方法");
}
}
class B extends A{
static {
System.out.println("子类的静态代码块");
}
{
System.out.println("子类的实例化代码块");
}
public B(){
System.out.println("子类的构造方法");
}
}
public class text {
public static void main(String[] args) {
B b=new B();
}
在继承中,先加载父类静态代码块,后加载子类静态代码块,后加载父类的实例化代码块和构造方法,最后加载子类的实例化代码块和构造方法
静态首先加载(先父类后子类)
全部加载父类(先实例后构造)
全部加载子类(先实例后构造)
2.重写父类方法
子类可以继承父类的公开方法,但有时,我们需要在子类中对继承的方法进行修改,也就是对父类方法进行重写
子类中重写的方法需要和父类中被重写的方法有一样的方法名,参数列表和返回值类型
(方法的重载:返回值类型不影响,要求方法名相同,参数类型和个数不同)
class Animal {
public void eat() {
System.out.println("dog can eat");
}
public void fun(){
System.out.println("hi");
}
}
class Dog extends Animal{
public void eat() {//重写父类的eat方法
System.out.println("汪汪爱吃骨头");
}
}
public class text {
public static void main(String[] args) {
Dog dog=new Dog();
dog.eat();
Animal animal=new Animal();
animal.eat();
}
}
定义了Dog类和Animal类,Dog类继承了Animal类,并对父类的eat方法进行了重写,从运行结果可以看到,调用的是 Dog类的eat方法时,执行Dog类中的重写的eat方法。调用Animal类的eat方法时,执行Animal类中的eat方法。
- 子类重写父类方法时,不能使用比父类方法更加严格的访问权限
- 静态方法不能重写
- private或者final修饰的方法不能重写
3.super关键字
由上面我们可以发现,在子类对象中没有办法直接访问父类成员,可以用super关键字来访问父类的成员变量,成员方法和构造方法
1.使用super关键字调用父类的成员变量和成员方法
super.成员变量
super.成员方法
class Animal {
int age = 9;
public void eat() {
System.out.println("dog can eat");
}
public void fun() {
System.out.println("hi");
}
}
class Dog extends Animal{
public void eat() {
System.out.println("汪汪爱吃骨头");
super.eat();//调用了父类的eat方法
}
public void print(){
System.out.println(super.age);//调用了父类的成员变量
}
}
public class text {
public static void main(String[] args) {
Dog dog=new Dog();
dog.eat();
dog.print();
}
}
2.使用super关键字调用父类的构造方法
使用super关键字调用父类的构造方法,具体格式如下
super(【参数1,参数2……】)
class Animal {
public Animal(String name){
System.out.println(name);
}
public Animal(int age){
System.out.println(age);
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
public Dog(int age) {
super(age);
}
}
public class text {
public static void main(String[] args) {
Dog dog=new Dog(56);
new Dog("汪汪");
}
}
定义了类Animal和类Dog,Dog类继承了父类Animal,其中Dog类和Animal类内部分别都有两个构造方法,内部的两个方法之间构成了重载。
在创建Dog类对象时一定会调用Dog类的构造方法, Dog dog=new Dog(56);实例化对象时执行了Dog内部的构造方法,从而调用了super(age)方法,调用父类Animal中有参数age的构造方法
注意:借用super关键字来调用父类的构造方法的代码必须在子类构造方法的第一行,且只能使用一次
java的每一个类中至少会有一个构造方法:如果说在一个类中没有定义构造方法,系统会自动地为这个类创建一个默认的构造方法,这个默认的构造方法没有参数,方法体中也没有任何代码。一旦为该类定义了构造方法,系统将不会提供默认的无参数构造方法
注意:子类的构造方法一定会调用父类的某一个构造方法 :子类可以提供super关键字指定调用某一个父类的构造方法,如果没有使用super关键字,实例化子类对象时,会默认调用父类无参的构造方法
class Base {
Base() {
System.out.print("Base");
}
}
public class Alpha extends Base {
public static void main( String[] args ) {
new Alpha();//调用子类构造方法,从而一定调用父类构造方法
new Base();
}
}
这个题中,子类没有自己定义的构造方法,这时系统会默认存在一个构造方法,public Alpha{ }。实例化对象时,会调用子类的构造方法,然后子类的构造方法一定会调用父类的构造方法,没有super关键字指定时,默认调用父类无参数的构造方法。
- 如果说父类定义了构造方法,那么不会调用系统默认的构造方法(这个默认的构造方法没有参数,方法体中也没有任何代码)。
- 只要父类定义的构造方法有一个没有参数,没有使用super关键字指定调用哪一个构造方法时,会默认调用这个定义的没有参数构造方法
- 如果父类定义的构造方法有参数,且不存在没有参数的构造方法,这时候就必须使用super关键字来调用父类有参数的构造方法,否则会编译错误
类的多态
1.对象的类型转换
向上转型
父类引用 引用子类对象
public static void main(String[] args) {
Dog dog=new Dog();
Animal animal=dog;//animal引用了子类对象
//等价于:
Animal animal=new Dog();//父类引用 引用了子类对象
}
发生时机
- 直接赋值
public static void main(String[] args) {
Animal animal1=new Dog();//父类引用 引用了子类对象
}
- 方法传参
class Dog extends Animal {
public void eat(Animal animal){
}
}
public class text {
public static void main(String[] args) {
Dog dog=new Dog();
dog.eat(dog);
}
- 方法返回值
lass Dog extends Animal {
public Animal eat(){
Dog dog=new Dog();
return dog;
}
}
父类引用只能引用父类的成员,不能引用子类特有的方法
class Animal {
public void animal(){
System.out.println("吃");
}
}
class Dog extends Animal {
public void dog(){
System.out.println("汪汪吃骨头");
}
}
public class text {
public static void main(String[] args) {
Animal animal = new Dog();//父类引用 引用了子类对象
animal.dog();//error,父类引用只能引用父类里面的成员
}
}
报错 在把子类对象当作父类使用时,不能用父类直接引用子类特有的方法
创建Dog对象时指向了Animal父类类型,这样创建的Dog类对象会自动向上转型为Animal类,通过dog这个引用调用Dog内部特有的dog方法,由于dog方法是Dog类特有的,使用Animal类对象无法调用这个方法
向下转型
为了解决以上的问题,要进行强制类型转化
public class text {
public static void main(String[] args) {
Animal animal = new Dog();//父类引用 引用了子类对象
Dog dog=(Dog)animal;//向下转型
dog.dog();//error
}
}
anmial对象本质应该是Dog类型,被向上转型为Animal类型, Dog dog=(Dog)animal;//向下转型,anmial对象强制转化为Dog类型,这样可以调用子类的特有方法。
向下转型时不能强制转化为其他类型
class Animal {
public void animal(){
System.out.println("吃");
}
}
class Dog extends Animal {
public void dog(){
System.out.println("汪汪吃骨头");
}
}
class Bird extends Animal{
int a=10;
}
public class text {
public static void main(String[] args) {
Animal animal = new Dog();//父类引用 引用了子类对象
Bird bird=(Bird)animal;//向下转型
}
}
因为 Animal animal = new Dog();anmial对象本质是Dog类型,但是向上转型为Animal类型。Bird bird=(Bird)animal;anmial对象本质是Dog类型,不能向下转型为Bird类型,只能向下转型为Dog类型
instanceof关键字
为了避免上述异常的发生,Java提供了一个关键字instanceof来判断一个对象是否是一个类的对象或者子类对象
public class text {
public static void main(String[] args) {
Animal animal = new Dog();//父类引用 引用了子类对象
if(animal instanceof Bird){
Bird bird=(Bird) animal;
System.out.println("anmial 是 Bird类的对象或者子类对象");
}
System.out.println("anmial 不是 Bird类的对象或者子类对象");
}
public static void main3(String[] args) {
}
}
2. 动态绑定
class Animal {
public void eat(){
System.out.println("吃");
}
}
class Dog extends Animal {
public void eat(){
System.out.println("汪汪吃骨头");
}
}
public class text {
public static void main(String[] args) {
Animal animal=new Dog();//父类引用 引用了子类对象
animal.eat();
}
}
子类中对父类的方法进行重写,向上转型访问重写的方法,执行的是子类中重写的方法,这里发生了动态绑定
动态绑定:1.向上转型 2.通过向上转型调用重写方法
也就是父类调用重写方法---执行子类里的方法
把一个方法与其所在的类/对象关联起来叫做方法的绑定。
编译时多态(静态绑定):利用方法的重载实现,根据参数不同来确定执行哪一个方法
运行时多态(动态绑定):利用子类对父类的重写,在运行期间通过具体执行代码确定执行哪一个方法
父类里含有构造方法
class Animal {
public Animal(){
eat();//调用的eat方法是父类的还是子类的---子类
}
public void eat(){
System.out.println("吃食物");
}
}
class Dog extends Animal {
public void eat(){//对父类的eat方法重写
System.out.println("狗狗吃骨头");
}
}
public class text {
public static void main(String[] args) {
Dog dog= new Dog();//调用了父类的构造方法
}
}
main方法中,创建Dog实例的时候,会调用子类的构造方法,子类的构造方法一定会调用父类的构造方法。
父类的构造方法里调用了eat方法,而eat方法在子类里面重写了,发生动态绑定,调用的是子类的eat方法(从父类调用重写方法---动态绑定)
尽量不要在构造方法里面调用方法,一旦这个方法被子类重写了,就会触发动态绑定,产生意料之外的结果
3.多态的概念
Java中,多态指不同类的对象在调用同一个方法时呈现的多种不同行为
class Draw {
public void draw() {
System.out.println("这是父类");
}
}
class Flower extends Draw{
@Override
public void draw(){
System.out.println("❀");
}
}
class Square extends Draw{
@Override
public void draw(){
System.out.println("■");
}
}
class Triangle extends Draw{
@Override
public void draw(){
System.out.println("🔺");
}
}
public class text {
public static void Draw(Draw Draw) {
Draw.draw();
}
public static void main(String[] args) {
Draw(new Flower());
Draw(new Square());
Draw(new Triangle());
}
}
在子类内部都对父类的draw()方法进行了重写,调用 Draw()方法,将子类对象向上转型并调用重写方法,发生了动态绑定,导致同一个方法产生了不同的结果,这就是多态