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

抽象类的作用

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


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

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

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

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

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

目录
相关文章
|
30天前
|
Java
Java 面向对象编程的三大法宝:封装、继承与多态
本文介绍了Java面向对象编程中的三大核心概念:封装、继承和多态。
106 15
|
3月前
|
Java
在Java中,接口之间可以继承吗?
接口继承是一种重要的机制,它允许一个接口从另一个或多个接口继承方法和常量。
286 60
|
4月前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
94 2
|
9月前
|
Java
java面向对象——包+继承+多态(一)-2
java面向对象——包+继承+多态(一)
67 3
|
9月前
|
SQL Java 编译器
java面向对象——包+继承+多态(一)-1
java面向对象——包+继承+多态(一)
45 2
|
存储 Java C语言
Java面向对象进阶5——包和final(含源码阅读)
包在操作系统中其实就是一个文件夹。包是用来分门别类的管理技术,不同的技术类放在不同的包下,方便管理和维护
113 0
Java面向对象进阶5——包和final(含源码阅读)
|
存储 Java
Java SE基础知识详解第[8]期—面向对象进阶(包、权限修饰符、抽象类、接口)
Java SE基础知识详解第[8]期—面向对象进阶(包、权限修饰符、抽象类、接口)
Java SE基础知识详解第[8]期—面向对象进阶(包、权限修饰符、抽象类、接口)
|
4天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
45 14
|
7天前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
37 13

热门文章

最新文章