• 面向对象3


    面向对象-多态

    1. 课程介绍

    Ø 1.super(掌握)

    Ø 2.多态(掌握)

    Ø 3.引用类型的转换(掌握)

    Ø 4.final(掌握)

    Ø 5.单例模式[掌握:面试题]

    2. super(简单)

    2.1 Super引入前示例

    1. 假设关于继承的理解(示例):

    1) 目前理解的子类可以继承父类哪些东西 :  非私有的字段和方法【主要是从访问权限的角度,局限性】

    2) 看示例:做学生管理系统,需要登陆的功能: 涉及 ,学生账号,老师账号  

    都是账号有共同属性 account  password 但是学生账号和老师账号都有独有的属性行为

    3) 类的设计

    a) 设计一个用户类User : 包含:   用户名  密码 字段

    b) 让学生类和老师类继承 User用户类

    4) 规范写法:User类中的用户名及密码字段应该私有化提供get set方法

    5) 问题:父类User中的字段私有化,子类学生类和老师类能够继承到用户名和密码吗?

    a. 从目前理解(主要是从访问权限来看),子类是继承不到私有成员的;

    b. 是否应该在子类中再定义用户名和密码字段呢?    不应该!

    c. 结论:子类对象中应该没有用户字段及密码字段可以存值

    6) 就按照上面的写法,验证子类中是否具有父类中的属性这样的一个特点

     

    7) 类设计:  账号类User,学生账号类StudentUser,老师账号类TeacherUser

    class User{//账号类

    private String account;

    private String password;

    public void setAccount(String account){

    this.account = account;

    }

    public String getAccount(){

    return this.account;

    }

    public void setPassword(String password){

    this.password = password;

    }

    public String getPassword(){

    return this.password;

    }

    }

    class StudentUser extends User{//学生账号类 extends 账号类

     

    }

    class TeacherUser extends User{//老师账号类 extends 账号类

    }

    8) 测试类

    //测试类

    class Test {

    public static void main(String[] args) {

    StudentUser stu = new StudentUser();

    stu.setAccount("嚣张");

    stu.setPassword("123456");

    System.out.println("账号:"+stu.getAccount()+", 密码:"+stu.getPassword());

    }

    }

    ---------- Java ----------

    账号:嚣张, 密码:123456

     

    输入完成 (耗时0秒) - 正常终止

    9) 分析:发现StudentUser类,虽然没有继承到字段,但是还是在通过set  get方法正常的存值取值,感觉还是用到User类中的account  password字段的,为什么且听super分析

    2.2 回顾this

    1. this的概念:this表示当前对象,持有当前对象的地址

    2. 判断this当前对象是谁?

    a) 官方:this所在的函数正在被谁调用,this就指代谁

    b) 民间:this当前被哪个对象持有this就指代谁

    3. this的使用场景

    a) 通过this访问当前对象中的成员(字段,方法);区分成员变量与局部变量的二义性

    b) this(当前对象)看成是一个数据, 就可以作为值返回,作为参数传递...

    c) 使用场景-2 : 本类中构造方法的之间的方法体第一句相互调用;

    i. this();  调用本类中无参数的构造方法;

    ii. this(123); 调用本类中具有一个int参数的构造方法;

    2.3 什么是super

    1. 什么是Super代码分析示例

    //测试类

    class Test {

    public static void main(String[] args) {

    StudentUser stu = new StudentUser();

    stu.eat("大花花");

    }

    }

    class User{

    String name = "小花花";

    }

    class StudentUser extends User{

    String name = "中花花";

    void eat(String name){

    System.out.println("name = "+name);

    System.out.println("this.name = "+this.name);

    System.out.println("super.name = "+super.name);

    }

    }

    2. super的概念:在子类中表示父类的对象

    3. 区别this:super不持有父类对象地址

    //子类方法测试打印super

     

    System.out.println(super);

     

    Test.java:17: 错误: 需要';'

    System.out.println(super);

                              ^

    Test.java:19: 错误: 解析时已到达文件结尾

    }

     ^

    4 个错误

     

    输出完成 (耗时 0 秒) - 正常终止

    2.4 super的使用场景

    1. 前提:super访问父类的成员,都必须是在有访问权限的条件之下;

    2. super访问父类对象中的字段  普通方法;

    class User{//父类User

    String name = "小花花";

    void login(){

    System.out.println("User login");

    }

    }

    class StudentUser extends User{//子类 StudentUser 

    String name = "中花花";

    void eat(String name){

    System.out.println("super.name = "+super.name);//super访问父类字段

    super.login();//super访问父类普通方法

    System.out.println("name = "+name);

    System.out.println("this.name = "+this.name);

     

    }

    }

    class Test {//有主方法的测试类Test

    public static void main(String[] args) {

    StudentUser stu = new StudentUser();

    stu.eat("大花花");

    }

    }

    ---------- Java ------------------------------------------------------

    super.name = 小花花

    User login

    name = 大花花

    this.name = 中花花

     

    输出完成 (耗时 0 秒) - 正常终止

    3.  在子类的构造方法体第一句访问父类的构造方法;  最常用的!!!!!!!!!!!!

    1) super();  调用父类无参数的构造方法;

    2) super(123); 调用父类中具有一个int参数的构造方法;

    2.5 super的特殊使用场景及分析

    1. 特殊使用场景 :在子类的构造方法第一句,

    a) 如果没有显示的写出对于父类构造方法的调用,那么会隐式的调用父类的无参数的构造方法!

    b) 如果有显示的写出对于父类构造方法的调用,那么会隐式的调用父类的无参数的构造方法,就不存在了!

    2. 特殊使用场景结论:子类的构造方法中一定会调用到父类的构造方法

    3. 特殊使用场景测试

    class User{

    User(){

    System.out.println("隐式无参父类User");

    }

    User(String name){

    System.out.println("显示有参式父类User");

    }

    }

    class StudentUser extends User{

    StudentUser(){//隐式调用父类的无参数的构造方法

    System.out.println("隐式子类StudentUser");

    }

    StudentUser(String name){//显示的写出隐式的调用就存在了

    super(name);

    System.out.println("显示子类有参StudentUser");

    }

    }

    //测试类

    class Test {

    public static void main(String[] args) {

    System.out.println("----------隐式父类无参数构造方法调用---------");

    new StudentUser();

    System.out.println("----------显式父类有参数构造方法调用----------");

    new StudentUser("小王八");

    }

    }

    4. super特殊用途分析:无论如何,子类的构造方法一定会存在对于父类构造方法的调用【难点:重在理解】

    a) 私有化字段在子类getset可以用到的原因

    b) javaBean为什么一定要提供一个无参数的构造方法

    c) 为什么子类可以继承父类的东西寄特性

        

    2.6 super小结

    1. super概念

    2. super的作用

    3. super的特殊用途

    4. super的特殊用途分析

    3. 多态(要点,难点)

    3.1 多态的引入

    1. 概念引入:重点代码思想

    2. 简单理解一种事物多种形态

    3. 示例:

    a) 今天晚上请大家吃大餐 : 思想上的大餐 , 凉开水

    b) 同学们准备买一辆车:   宾利 兰博基尼  柯塞尼格,老谭二八杠..

    c) 同学们要上天 :  坐飞机,

    4. java代码中的体现

     

    1) 一个动物就是一个蛇   

    2) 一个动物就是一个猪

    3) 一个动物就是一个人

    4) 一个人就是一个动物:Animal aml = new Person();//一个动物变量中,可能存放子类对象的多可能

    5) 一个猪就是一个动物:Animal aml = new Pig();

    6) 一个蛇就是一个动物:Animal aml = new Snak();

    7) 一个人就是一个猪

    8) 一个蛇就是一个人

    9) 一个猪就是一个蛇

    3.2 什么是多态

    1.一个人就是一个动物:

    (1) Animal aml = new Person();//一个动物变量中,可能存放子类对象的多种可能

    2.理解:使用aml:编译是看的是Animal类型,运行时看的是实际存放的对象[真实类型]

    3.官方概念:编译时跟运行时类型不一致就产生了多态

    4.民间理解:父类类型的变量,存放子类类型的对象,可能存放子类类型的对象有多种可能

    5.多态存在的前提:必须有继承关系

    3.3 多态方法调用编译运行过程[重点]

    class Animal{

    void eat(){

            System.out.println(“食物........”);

    }

    }

    class Person extends Animal{

     

    }

    //----------------------------------测试类-----------------------

    class Test {

    public static void main(String[] args) {

    Animal aml = new Person();

         aml.eat();

    }

    }

    1. 上面两句代码的编译,运行过程:

    1) 编译时 :  

    a) aml 编译时,看的是父类类型,会在父类类型中eat方法

    b) 如果没有找到,会继续向上找[aml编译时父类类型]

    i. 找到:编译通过

    ii. 找不到:编译报错

    iii. 注意:是不会向下找的[aml编译时子类类型]

    2) 运行时 :

    a) 先到运行时类型[Person]中找eat方法,

    i. 如果找到:就执行,

    ii. 没找到:向上到父类中找并执行

    2. 思考 : 有没有可能编译通过了,而运行找不到方法...    不可能!

    3.4 编译时与运行时的几种情况分析

    1. 父类中有一个方法,子类覆写了

    2. 父类中有一个方法,子类没有

    3. 父类中没有,子类有一个方法

    4. 父类子类都没有

    ----------- 上面都是实例方法,下面来一个静态方法---------------------------------

    5. 静态方法

    6. 字段没有覆写一说

    分析:

    1. 实际开发中一般不会再子类中定义一个和父类同名的字段,

    2. 如果是有这样的情况存在,

    1) 如果编译时类型父类的类型,取值是父类里面字段的值;

    2) 如果编译时类型子类的类型,取值是子类里面字段的值;

    3.5 多态方法调用以及参数传递应用示例[重点]

    class Dog{//父类类型Dog

    void eat(){

    System.out.println("吃食物");

    }

    }

    class DDog extends Dog{//子类类型DDog

    void eat(){

    System.out.println("哈根达斯");

    }

    }

    class XDog extends Dog{//子类类型XDog

    void eat(){

    System.out.println("吃牛排喝红酒");

    }

    }

    class Person{//人类:定义喂狗方法

    void feedDog(Dog dog){

    dog.eat();

    }

    }

    //------------------------------测试类-----------------------------------

    class Test {

    public static void main(String[] args) {

    Dog ddog = new DDog();

    XDog xdog = new XDog();

     

    Person pson = new Person();

    pson.feedDog(ddog);

    pson.feedDog(xdog);

    }

    }

    3.6 多态体现的几种情况

    1. 如上代码多态的体现的本质:都是父类类型的变量存放子类类型的对象

    2. Dog dog = new XDog();//核心本质

    3. 方法参数传递:方法形参父类类型,允许传子类类型对象

    i. Dog dog = new XDog();

    ii. XDog xdog = new XDog();

    iii. void feedDog(Dog dog){   }//此方法运行传递上面两个参数dog   或者  xdog   :  本质传递的都是子类对象

    4. 方法返回值

    Dog getDog(){//此方法返回值类型为父类类型Dog

    return new XDog();//允许返回子类类型

    }

    思考:方法内部返回值的类型一定是,方法返回值位置指定的类型么?

    5. 多态的好处:屏蔽了不同子类之间实现的差异

    6. 加强面向对象编程思想:

    a) 此处体现java语言设计是想,希望将现实生活中的对象与对象之间的关系在计算机系统得以体现

    3.7 多态小结

    1. 多态的概念

    2. 编译时运行时过程

    3. 多态的用途【多态的方法参数传递】

    (1) 结合面向对象编程思想及执行流程分析

    4. 多态在代码中如何体现的

    5. 多态的好处

    6. 加强面向对象编程思想的认识

    4. 引用类型转换

    4.1 引用类型转换引入

    class Cat{

    void eat(){}

    }

    class TomCat extends Cat{

    void say(){}

    }

    class CoffeeCat extends Cat{

    void drink(){}

    }

    //测试类

    class Test {

    public static void main(String[] args) {

    Cat cat = new Tomcat();

    cat.drink();//编译报错

    }

    }

    1.分析:类设计:提取公共属性,子类存在特有属性

    2.示例:猫[Cat]都会吃,但是汤姆猫[TomCat]会说话,咖啡猫[CoffeeCat]会喝咖啡

    3.多态使用存在可能:

    a) Cat cat  = new TomCat();

    b) cat.say();//编译时报错:say动态属性在,Cat中不存在

    4.2 为什么需要引用数据类型转换

    1. 如上示例:明知cat里面存放的是Tomcat,但是因为多态编译不能通过,不能使用,需要转换成Tomcat真实类型,即可以使用

    4.3引用数据类型转换的两种情况

    1. 明确:数据类型转换存在两种,情况,大转小:小转大

    1) 子类类型转父类类型:小转大

    Cat cat = new TomCat();

    double d = 1;

    2) 父类类型转子类类型:大转小

    TomCat  tc =(TomCat)cat;

    2. 在引用数据类型中:父类是较大的数据类型,子类是较小的数据类型

    4.4 引用数据类型转换的注意事项

    class Cat{

    void eat(){}

    }

    class TomCat extends Cat{

    void say(){}

    }

    class CoffeeCat extends Cat{

    void drink(){}

    }

    //测试类

    class Test {

    public static void main(String[] args) {

             Cat cat = new Tomcat();

             CoffeeCat cc = (CoffeeCat)cat;

      cc.drink();

    }

    }

    1. 如上代码示例分析;

    a) cat可能是传来的参数:在使用的时候不知道存放的是Tomcat,误以为存的是CoffeeCat类型

    b) 红色代码使用多态:绿色代码引用类型数据转换:cc.drink();

    c) 编译只看类型不看值,如上代码编译都不会有问题!

    d) 但是:运行时反应的是真实类型,绿色代码等于要将TomCat 变成 CoffeCat 显然不行

    2. 因此引用数据类型转换,最好在转换之前先判断类型在转换

    3. 判断类型的方式

    a) 获得运行时类型  Object 中getClass();方法

    b) 类型判断运算符  instanceof

    4. 判断类型示例

    a) 获得运行时类型  Object 中getClass();方法

    class Test {

    public static void main(String[] args) {

    Cat cat = new TomCat();

    String name = cat.getClass().getName();//获取到类型的名字String类型的值

    if(name.equals("CoffeeCat")){//比较是否羽CoffeeCat字符串一致

    CoffeeCat cc = (CoffeeCat)cat;

    cc.drink();

    }else{

    System.out.println("友好提示:小哥哥类型错了");

    }

    }

    }

    b) 类型判断运算符  instanceof

    //测试类

    class Test {

    public static void main(String[] args) {

    Cat cat = new TomCat();

    if(cat instanceof CoffeeCat){

    CoffeeCat cc = (CoffeeCat)cat;

    }else{

    System.out.println("友情提示:小姐姐你的类型错了");

    }

    }

    }

    5. instanceof运算符

    Systme.out.println(cat instanceof Cat);//true

    Systme.out.println(cat instanceof TomCat);//true

    Systme.out.println(cat instanceof Object);//true

    Systme.out.println(cat instanceof CoffeeCat);//false

    System.out.println(tom instanceof CoffeeCat);////编译报错,不存在继承关系,不兼容,完全相关类型:编译器只看类型不看值

    Systme.out.println(cat instanceof String);//编译报错,不存在继承关系,不兼容,完全相关类型:编译器只看类型不看值

    4.5引用数据类型转换小结

    1. 为什么需要引用数据类型转

    (1) 明知道多态父类类型装的是子类对象,但是子类特性,父类对象访问,编译报错,需要转换成真实类型  

    (2) Cat cat = new TomCat();   cat.say();//父类Cat中没有say属性 编译报错

    2. 数据类型转换的两种情况

    (1) 大赚小 TomCat tc = (TomCat)cat;

    (2) 小转大 Cat cat = new TomCat();

    (3) 父类大子类小 :  父类类型兼容子类类型

    3. 数据类型转换的注意事项

    (1) 在转换之前要进行类型判断

    (2) 两种判断方式

    4. instanceof运算符的运用

    (1) 只看类型不看值,不存在继承关系的,编译不通过

    5. final

    5.1 final是什么

    1. final  :  Java中的一个关键字,修饰符:表示的意思:最终的,不可变的,不可拓展的!

    2. 如何学习关键字修饰符

    a) 关键字的含义

    b) 可以修饰什么东西

    c) 修饰之后有什么效果

    5.2 final可以修饰的东西

    1. 外部类:可以

    2. 普通方法:可以

    3. 成员字段:可以

    4. 局部变量:可以

    5. 内部类:可以 [ 暂时不学 ]

    6. 构造方法:不可以

    5.3 final修饰类的效果

    1. final修饰类:最终类,不可拓展的类,太监类 :

    2. 比如说: String类  Integer等包装类 是使用final修饰的;

    final class Cat{

    }

    class TomCat extends Cat{

    }

    ---------- Javac ----------

    Test.java:10: 错误: 无法从最终Cat进行继承

    class TomCat extends Cat{

                         ^

    1 个错误

    输出完成 (耗时 0 秒) - 正常终止

    3.  思考:final修饰的类有不有父类呢    

    5.4 final 修饰普通方法的效果

    1. final修饰普通方法:最终的方法,不可拓展的方法:

    class Cat{

    final void eat(){}

    }

    class TomCat extends Cat{

    @Override

    void say(){}

    }

    ---------- Javac ----------

    Test.java:12: 错误: 方法不会覆盖或实现超类型的方法

    @Override

    ^

    1 个错误

     

    输出完成 (耗时 0 秒) - 正常终止

             

    2. 目前不能被覆写的方法 有哪些?

    1) 使用final修饰的方法

    2) 使用static修饰的方法

    3) 使用private修饰的方法

    5.5 final 修饰变量

    1. final修饰变量:表示最终的变量 :

    //测试类

    class Test {

    final static int i = 10;

    public static void main(String[] args) {

    final int j = 20;

    i = 30;

    j = 40;

    }

    }

    2. final 修饰变量一般使用  public static final double PI = 3.14159265; 构成类中的全局常量仅供使用

    3. final修饰变量拓展及堆栈分析

    //测试类

    class Test {

    public static void main(String[] args) {

    final Student stu = new Student();

    stu.name = "小王";//可以因为修饰的不是name字段

    System.out.println(stu.name);

    stu = new Student();//不可以因为修饰的是stu变量

    }

    }

    ---------- Javac ----------

    Test.java:7: 错误: 无法为最终变量stu分配值

    stu = new Student();//不可以因为修饰的是stu变量

    ^

    1 个错误

     

    输出完成 (耗时 0 秒) - 正常终止

    4. 堆栈分析

     

    5.6 final小结

    1. final关键字修饰符最终的不可拓展的不可变的

    2. final可以修饰那些内容

    3. final修饰类的效果

    4. final修饰方法的效果

    5. final 修饰变量的效果

    6. final修饰变量的拓展分析

    1. 单例模式(面试题)

    2.1 什么是单例模式

    1. 单例模式名词解释

        一个

        实例instance(对象)

    模式   就是一种设计模式[为了解决某类问题,而提出的比较好的解决方案]

    2. 单利模式概念:需要设计一个类,达到的效果: 此类在整个应用中只存在一个对象

    1) 整个应用:

    a. java代码写好的一个应用软件,系统:java代码开发结果,运行在JVM[处于]JRE运行环境}

    2) 应用软件:java代码开发的

    3) 单利模式好处:节省了系统资源,节省了内存空间

    4) 单利模式对象怎么用 : 如果系统很多位置都会用到该对象,通过该对象的引用地址对其引用

    3. 如何达到这样的效果:设计一个类,这个类的对象,永远只有一个?

    1) 设计思路:

    a. 设计一个类  class A{}

    b. 对象怎么来的?new A();    new A();     new A();  ...   调用一次构造方法就得到一个对象

    c. 把构造方法私有化,本类的外部就不能够随意的访问创建对象了

    d. 思考 : 一个类的所有构造方法都被私有化,就不能够创建对象了,说法正确吗?不正确

    a) 外部不能创建,自己内部可以创建

    e. 可以在A类内部创建好一个,并保存起来,别人需要对象给它一个地址

    2.2 设计实现之饿汉模式

    1.  设计实现之饿汉模式

    1) 构造方法私有化

    2) 在类的内部创建一个对象

    3) 使用一个字段保存起来

    4) 提供一个方法允许外部使用该方法访问该字段

    5) 提供的方法必须静态修饰,因为外部不能创建对象

    6) 外部通过方法访问instance 字段,方法静态修饰,所以字段必须静态修饰

    7) 字段不私有化,别人可以通过类名.instance修改子字段值,所以必须私有化

    class A{

    private A(){}

    private A instance = new A();

    public A getInstance(){

    return instance;

    }

    }

    分析:

    1 单例模式的类也是一个普通的类,其中也可以有其他的字段  方法等

    2 上面代码中,instance 对象是A类被加载[把类放到JVM的过程中]的时候创建的

    3 问题:如果A类中其他的字段和方法很多,创建对象的过程比较长,类加载会比较慢,有可能加载之后,很长时间其实都没有人来获得对象:加载慢,消耗系统资源

    4 需求:有没有一种方式:在类加载的时候先不创建对象,而是在有人第一次来调用方法获得对象的时候才创建一个对象,之后需要保存起来,以后再有人调用就不用创建对象

    2.3 设计实现之懒汉模式

    1. 设计实现之懒汉模式

    1) 明确:应用程序在第一次调用方法获取单利模式对象的时候创建对象

    2) 构造方法私有化

    3) 设置一个A类类型的字段私有化不初始化值

    4) 提供一个方法允许外部使用该方法访问该字段

    5) 外部不能创建对象,所以方法必须static修饰

    6) 什么时候是第一次【instance == null】

    7) 当第一调用的时候判断

    a. 如果instance == null :初始化instance

    b. 如果instance != null : 直接返回instance

    class A{

    private A(){}

    private static  A instance;

    public static A getInstance(){

    if(instance == null){

    instance = new A();

    }

    return instance;

    }

    }

    2.4懒汉模式存在线程安全问题分析    

    1. 懒汉模式存在线程安全问题:有可能返回的不是同一个对象!

     

    2. 线程A1,A2对代码的访问都是独立的

    3. 线程A1,执行到位置A1  切换到A2 ,线程A2执行到位置A2,

    4. A1线程继续执行,创建对象初始化instance,返回!切换到线程A2,继续执行又创建了对象给instance赋值,instance原来A1创建的对象地址值,被覆盖,A2会返回自己新创建的地址值。A1,A2 线程返回的不是同一个对象

    5. 解决办法:加锁,A1,A2  必须访问的是同一把锁!

    2.5使用场景

    1. 主要设计在工具类中,例如Arrays中!static静态修饰方法

    2. 单利模式设计在一个工具类中:好处!不用频繁的去创建创建对象

    3. 频繁创建对象的不好之处:消耗系统资源,对象过多占用内存

    4. JavaEE:JDBC数据裤链接的时候,还会使用到单利模式  

    2.6 小结

    1. 知道什么是设计模式

    2. 理解什么单利模式【结合对象创建堆栈分析】

    3. 单利模式之饿汗模式设计及缺点

    4. 单利模式之懒汉模式设计及缺点

    5. 单利模式必须会写[7~8初级面试默写]

     

    6. 课程总结

    1.1. 重点

    1. super

    2. 多态

    3. 引用类型转换

    4. final

    5. 单例模式

    1.2. 难点

    1. super特殊用途分析

    2. 多态的应用,代码逻辑稍微较多。

    3. 引用类型转换

    1.3. 如何掌握?

    1. 勤加练习...

    7. 课后练习[30分钟]

    第一题:面向对象的三大特征是什么?

    第二题:谈谈对封装,继承,多态的理解?

    第三题:使用final修饰一个类,测试它能否被继承?测试final修饰一个字段,能否被修改字段里面的值?

    第四题:练习今天上课的所有的代码,并在代码上面说明清楚;

    8. 面试题

    谈谈你对面向对象的三大特征的理解?

  • 相关阅读:
    团队活动
    实力提升
    软工人必须要知道的几个工具
    博客/论坛:(技术分享)
    CSS怎样设置多个字体,设置多个字体的时候要注意什么
    canvas绘制bitmap全部填充(当bitmap的宽高小于绘制区域的时候)
    CSS字体
    weditor元素定位异常
    python ImportError: C extension: DLL load failed while importing strptim
    python:导入不同路径下相同名称的模块
  • 原文地址:https://www.cnblogs.com/xiaoruirui/p/11208312.html
Copyright © 2020-2023  润新知