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

抽象类的作用

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


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

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

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

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

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

目录
相关文章
|
14天前
|
Java Maven
【Java报错】显示错误“Error:java: 程序包org.springframework.boot不存在“
【Java报错】显示错误“Error:java: 程序包org.springframework.boot不存在“
35 3
|
1天前
|
存储 安全 Java
[Java基础面试题] Map 接口相关
[Java基础面试题] Map 接口相关
|
1天前
|
Java
【Java基础】详解面向对象特性(诸如继承、重载、重写等等)
【Java基础】详解面向对象特性(诸如继承、重载、重写等等)
5 0
|
7天前
|
Java 开发者
探索 Java 的函数式接口和 Lambda 表达式
【4月更文挑战第19天】Java 中的函数式接口和 Lambda 表达式提供了简洁、灵活的编程方式。函数式接口有且仅有一个抽象方法,用于与 Lambda(一种匿名函数语法)配合,简化代码并增强可读性。Lambda 表达式的优点在于其简洁性和灵活性,常用于事件处理、过滤和排序等场景。使用时注意兼容性和变量作用域,它们能提高代码效率和可维护性。
|
7天前
|
Java
Java接口中可以定义哪些方法?
【4月更文挑战第13天】
14 0
Java接口中可以定义哪些方法?
|
9天前
|
设计模式 Java
Java接口与抽象类
Java接口与抽象类
17 0
|
13天前
|
安全 Java 编译器
接口之美,内部之妙:深入解析Java的接口与内部类
接口之美,内部之妙:深入解析Java的接口与内部类
35 0
接口之美,内部之妙:深入解析Java的接口与内部类
|
Java
【JAVA SE】——包、继承、多态、抽象类、接口 ( 巨细!总结 )3
【JAVA SE】——包、继承、多态、抽象类、接口 ( 巨细!总结 )3
95 0
【JAVA SE】——包、继承、多态、抽象类、接口 ( 巨细!总结 )3
|
安全 Java 编译器
【JAVA SE】——包、继承、多态、抽象类、接口 ( 巨细!总结 )2
【JAVA SE】——包、继承、多态、抽象类、接口 ( 巨细!总结 )2
112 0
【JAVA SE】——包、继承、多态、抽象类、接口 ( 巨细!总结 )2
|
存储 Java 编译器
【JAVA SE】——包、继承、多态、抽象类、接口 ( 巨细!总结 )1
【JAVA SE】——包、继承、多态、抽象类、接口 ( 巨细!总结 )1
207 0
【JAVA SE】——包、继承、多态、抽象类、接口 ( 巨细!总结 )1