java面向对象编程_包_继承_多态_重载和重写_抽象类_接口_this和super(2)

简介: java面向对象编程_包_继承_多态_重载和重写_抽象类_接口_this和super(2)

访问修饰限定符

java中的字段和方法的四种访问权限


public可以在不同包中的类访问!


protected不同包中继承关系访问!


默认包访问权限,只能在同一包中的类中访问!


privated只能在同一个类中访问

image.png


image.png

多态

在编程语言和类型论中,多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口。 多态类型(英语:polymorphic type)可以将自身所支持的操作套用到其它类型的值上。多态(百度词条)


向上转型

子类对象赋值给了父类引用


该对象只能访问父类的字段和方法!


直接赋值

子类对象赋值给了父类引用


class Animal{
    protected String name;
    protected int  age;
    public void eat(){
        System.out.println("animal eat()!");
    }
}
class Dog extends Animal{
    protected int height;
    public void running(){
        System.out.println("dog running()!");
    }
}
public class Test_1 {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal = new Dog();  //1.子类对象赋值给了父类引用
        Animal animal1 = new Dog(); //2.和 1 等价
        animal.eat(); //调用父类中的方法
        //error animal只能访问父类中的字段和方法!
        animal.height=1;
        animal.running();
    }
}

image.png

方法传参

image.png

//方法传参
class Animal{
    protected String name;
    protected int  age;
    public void eat(){
        System.out.println("animal eat()!");
    }
}
class Dog extends Animal{
    protected int height;
    public void running(){
        System.out.println("dog running()!");
    }
}
public class Test_2 {
    public static void func(Animal animal){
        animal.eat();
    }
    public static void main(String[] args) {
        Dog dog = new Dog();
        func(dog);
        func(new Dog()); //子类对象赋值给了父类引用!
    }
}

image.png

方法返回

//方法返回
class Animal{
    protected String name;
    protected int  age;
    public void eat(){
        System.out.println("animal eat()!");
    }
}
class Dog extends Animal{
    protected int height;
    public void running(){
        System.out.println("dog running()!");
    }
}
public class Test_2 {
    public static Animal func(){
       return new Dog();  //子类对对象返回给了父类引用!
    }
    public static void main(String[] args) {
       Animal animal = func();
       animal.eat();
    }
}

image.png

动态绑定

动态绑定就是,当子类重写了父类的方法时,向上转型后,对象调用与重写的方法,访问的是子类对象中的重写方法!


//运行时绑定
class Animal{
    protected String name;
    protected int  age;
    public void eat(){
        System.out.println("animal eat()!");
    }
}
class Dog extends Animal{
    protected int height;
    @Override
    public void eat() {
        System.out.println("dog eat()!");
    }
    public void running(){
        System.out.println("dog running()!");
    }
}
public class Test_2 {
    public static void main(String[] args) {
       Animal animal = new Dog();
       animal.eat(); //运行时绑定!
    }
}


image.png

为啥要叫运行时绑定呢?

难道说编译的时候没有绑定?

确实如此,当我们查看java的反汇编代码时就会发现,编译期间anmial调用的是自己的eat方法,但是运行时却绑定了子类的eat()!

image.png


java反汇编代码步骤:

找到,我们需要反汇编代码类的字节码文件

在命令符的窗口下,输入javap -c 类名代码 回车即可!


理解多态

我们想一想多态可以帮助我们做些什么!

bug郭的理解


运行时绑定使用场景

我们可以重写父类的方法,实现多态。调用重写的方法,一个父类可以有多个子类,不同的子类重写了不同的方法,实现了真正意义上的多态!

eg:打印图形

//类的实现者
class Shape {
    public void draw() {
        // 啥都不用干
    }
}
class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("○");
    }
}
class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("□");
    }
}
class Flower extends Shape {
    @Override
    public void draw() {
        System.out.println("♣");
    }
}
//类的调用者
public class Test_1{
    public static void main(String[] args) {
        Shape shape1 = new Flower();
        Shape shape2 = new Cycle();
        Shape shape3 = new Rect();
        drawShape(shape1);
        drawShape(shape2);
        drawShape(shape3);
    }
    // 打印单个图形
    public static void drawShape(Shape shape) {
        shape.draw();
    }
}

image.png


使用多态的好处是什么?


类调用者对类的使用成本进一步降低。

封装是让类的调用者不需要知道类的实现细节。

多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可。

因此, 多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降低。


能够降低代码的 “圈复杂度”, 避免使用大量的 if - else。

“圈复杂度” :就是代码中的分支和循环;

可扩展能力更强。

如果要新增一种新的形状,使用多态的方式代码改动成本也比较低。


向下转型

我们知道向上转型是子类对象赋值给了父类引用!

那向下转型莫不就是:父类对象赋值给了子类引用~


并不常见~了解一下即可!

//向下转型
class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}
class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    public void eat(String food) {
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
    public void fly() {
        System.out.println(this.name + "正在飞");
    }
}
public class Test_2 {
    public static void main(String[] args) {
        Animal animal = new Bird("鸽鸽"); //先借助向上转型
        animal.eat("脐橙");
        //animal.fly;  //error
        Bird bird;
        bird = (Bird)animal; //向下转型需要强转
        bird.fly();
    }
}

image.png

可以看到向下转型步骤比较繁琐,通常要借助向上转型!

而且我们需要确定是否为父子类关系!避免异常!


利用instanceof 关键字可以判定一个引用是否是某个类的实例。

若真返回true,若假返回false!

image.png

构造方法中调用一个重写的方法(一个坑!)

Plain Text

自动换行

xxxxxxxxxx

 

1

//坑

2

class B {

3

    public B() {

4

        // do nothing

5

        func();

6

    }

7

    public void func() {

8

        System.out.println("B.func()");

9

    }

10

}

11

class D extends B {

12

    private int num = 1;

13

    @Override

14

    public void func() {

15

        System.out.println("D.func() " + num);

16

    }

17

}

18

public class Test_3{

19

    public static void main(String[] args) {

20

        D d = new D();

21

    }

22

}

image.png

bug郭看了半天愣是没整明白为啥这个代码运行结果是这样!!!

我的理解:创建子类对象d会调用自己的构造方法,子类要先帮助父类构造,而父类中调用了子类重写的方法,动态绑定了;

我们并有执行子类中的 private int num = 1;语句!所以num此时并没有赋值!所以为0!

正解


构造D 对象的同时, 会调用B的构造方法.

B 的构造方法中调用了func方法, 此时会触发动态绑定, 会调用到D 中的func

此时 D对象自身还没有构造, 此时num 处在未初始化的状态, 值为 0.

结论: “用尽量简单的方式使对象进入可工作状态”, 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏但又及难发现的问题!


抽象类

基本语法

在刚才的打印图形例子中, 我们发现, 父类Shape中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstractmethod), 包含抽象方法的类我们称为 抽象类(abstract class)。

abstract class Shape { 
    abstract public void draw(); 
}

image.png


注意事项


抽象类不能直接实例化。

image.png


抽象类可以有一般类一样的字段和方法,语法相同。

abstract class Shape {
    protected  int longth;
    protected int wide;
    public int area(){
        return longth*wide;
    }
     abstract public  void draw();
}

image.png

抽象类的作用

抽象类存在的最大意义就是为了被继承。


抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类。然后让子类重写抽象类中的抽象方法。

有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?

确实如此. 但是使用抽象类相当于多了一重编译器的校验。

使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了,

使用普通类编译器是不会报错的。但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题!

目录
相关文章
|
1月前
|
Java
Java基础(13)抽象类、接口
本文介绍了Java面向对象编程中的抽象类和接口两个核心概念。抽象类不能被实例化,通常用于定义子类的通用方法和属性;接口则是完全抽象的类,允许声明一组方法但不实现它们。文章通过代码示例详细解析了抽象类和接口的定义及实现,并讨论了它们的区别和使用场景。
|
1月前
|
Java 测试技术 开发者
Java零基础-抽象类详解
【10月更文挑战第17天】Java零基础教学篇,手把手实践教学!
24 2
|
2月前
|
Java 测试技术 开发者
Java零基础-抽象类详解
【10月更文挑战第15天】Java零基础教学篇,手把手实践教学!
24 2
|
2月前
|
存储 Java 测试技术
Java零基础-多态详解
【10月更文挑战第10天】Java零基础教学篇,手把手实践教学!
27 4
|
2月前
|
Java 编译器 程序员
Java多态背后的秘密:动态绑定如何工作?
本文介绍了Java中多态的实现原理,通过动态绑定和虚拟方法表,使得父类引用可以调用子类的方法,增强了代码的灵活性和可维护性。文中通过具体示例详细解析了多态的工作机制。
53 4
|
2月前
|
Java 编译器
在Java中,关于final、static关键字与方法的重写和继承【易错点】
在Java中,关于final、static关键字与方法的重写和继承【易错点】
26 5
|
2月前
|
存储 Java 测试技术
Java零基础-多态详解
【10月更文挑战第1天】Java零基础教学篇,手把手实践教学!
29 1
|
2月前
|
安全 Java 数据安全/隐私保护
【一步一步了解Java系列】:探索抽象类与接口的秘密
【一步一步了解Java系列】:探索抽象类与接口的秘密
26 3
|
3月前
|
Java
Java——抽象类和接口
抽象类是一种不能被实例化的类,至少包含一个抽象方法(无实现体的方法),常用于定义一组相关类的共同特征,并强制子类实现特定方法。抽象方法不能被 `static` 或 `final` 修饰,且必须被重写。 接口则是一个完全抽象的类,用于规范类的行为。接口使用 `interface` 关键字定义,不能实例化,并且类与接口之间是实现关系。 内部类是在一个类内定义的类,分为成员内部类、静态内部类、局部内部类和匿名内部类。成员内部类可被修饰符修饰,静态内部类只能访问外部类的静态成员,局部内部类定义在方法内,匿名内部类则隐藏了名字,直接通过 `new` 关键字定义并实现接口或继承类。
24 5
Java——抽象类和接口
|
2月前
|
Java
Java中抽象类和接口有什么区别?
本文阐述了Java中抽象类和接口的区别,包括类型扩展、方法/属性访问控制符、方法实现、使用目的等方面的不同,并提供了抽象类和接口的使用示例,以及Java中其他类型的类(普通类、内部类等)的简介。
140 0
Java中抽象类和接口有什么区别?