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

抽象类的作用

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


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

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

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

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

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

目录
相关文章
|
4天前
|
Oracle 安全 Java
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
|
3天前
|
Java
SpringBoot java 一个接口,多个实现,客户定制化
SpringBoot java 一个接口,多个实现,客户定制化
10 0
|
4天前
|
Java API
Java9 Lambda 设计和实现问题之IntStream, LongStream 等类型特定的流接口基于 AbstractPipeline 是如何实现的
Java9 Lambda 设计和实现问题之IntStream, LongStream 等类型特定的流接口基于 AbstractPipeline 是如何实现的
|
7天前
|
安全 Java 数据处理
Java并发编程:解锁多线程的潜力
在数字化时代的浪潮中,Java作为一门广泛使用的编程语言,其并发编程能力是提升应用性能和响应速度的关键。本文将带你深入理解Java并发编程的核心概念,探索如何通过多线程技术有效利用计算资源,并实现高效的数据处理。我们将从基础出发,逐步揭开高效并发编程的面纱,让你的程序运行得更快、更稳、更强。
|
6天前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
26 7
|
5天前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
5天前
|
存储 监控 安全
一天十道Java面试题----第三天(对线程安全的理解------>线程池中阻塞队列的作用)
这篇文章是Java面试第三天的笔记,讨论了线程安全、Thread与Runnable的区别、守护线程、ThreadLocal原理及内存泄漏问题、并发并行串行的概念、并发三大特性、线程池的使用原因和解释、线程池处理流程,以及线程池中阻塞队列的作用和设计考虑。
|
3天前
|
存储 缓存 安全
深度剖析Java HashMap:源码分析、线程安全与最佳实践
深度剖析Java HashMap:源码分析、线程安全与最佳实践
|
9天前
|
消息中间件 Java 大数据
"深入理解Kafka单线程Consumer:核心参数配置、Java实现与实战指南"
【8月更文挑战第10天】在大数据领域,Apache Kafka以高吞吐和可扩展性成为主流数据流处理平台。Kafka的单线程Consumer因其实现简单且易于管理而在多种场景中受到欢迎。本文解析单线程Consumer的工作机制,强调其在错误处理和状态管理方面的优势,并通过详细参数说明及示例代码展示如何有效地使用KafkaConsumer类。了解这些内容将帮助开发者优化实时数据处理系统的性能与可靠性。
37 7
|
5天前
|
安全 Java
Java模拟生产者-消费者问题。生产者不断的往仓库中存放产品,消费者从仓库中消费产品。其中生产者和消费者都可以有若干个。在这里,生产者是一个线程,消费者是一个线程。仓库容量有限,只有库满时生产者不能存
该博客文章通过Java代码示例演示了生产者-消费者问题,其中生产者在仓库未满时生产产品,消费者在仓库有产品时消费产品,通过同步机制确保多线程环境下的线程安全和有效通信。