首页 > 其他 > 详细

面向对象(中)

时间:2021-03-28 21:47:43      阅读:23      评论:0      收藏:0      [点我收藏+]

面向对象三大特性

封装性

面向对象的特征一:封装与隐藏

为什么要引入封装性?

我们程序设计追求“高内聚,低耦合”。
高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
低耦合 :仅对外暴露少量的方法用于使用。

隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

问题引入

当我们创建一个类的对象以后,我们可以通过"对象.属性的方式,给对象的属性进行赋值。
这里,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没其他制约条件。
但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。比如:调用属性set方法时,需要避免再使用对象.属性的方式对属性进行赋值。则需要将属性声明为私有的(private)。此时,针对于属性就体现了封装性。

封装性思想具体的代码体现

  • 体现一:将类的属性私有化(private)同时,提供公共的(public)get/set方法来获取
  • 体现二:不对外暴露的私有的方法
  • 体现三:单例模式(将构造器私有化)
  • 体现四:如果不希望类在包外被调用,可以将类设置为缺省的。

四种权限修饰符

权限从小到大顺序为:private < 缺省 < protected < public

技术分享图片

注意区分default和protected,助记:protected可理解为孩子出远门需要保护

权限修饰符可用来修饰的结构

  • 4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
  • 修饰类的话,只能使用:缺省、public

继承性

为什么要有类的继承性?(继承性的好处)

减少代码的冗余,便于功能的扩展,为之后多态性的使用提供了前提

  • 子类继承了父类,就继承了父类的方法和属性。
  • 在子类中,可以使用父类中定义的方法和属性,也可以创建新的属于子类的属性和方法。
  • 在Java 中,继承的关键字用的是extends,即子类不是父类的子集, 而是对父类的扩展。

格式

//A:子类、派生类、subclass
//B:父类、超类、基类、superclass
class A extends B{}

注意

  • 子类从父类中只能获取到权限范围内的属性和方法,具体参考权限修饰符的内容。由于封装性的思想,子类不能直接访问父类中私有的(private)的成员变量和方法 ,但可以通过公共的get/set方法操作私有属性
  • Java类是单继承性
    • 一个类只能有一个父类
    • 一个父类可以派生出多个子类
  • 子父类是相对的概念,即多层性,父类还可以有父类。
  • 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
  • 子类继承父类以后,就获取了直接父类以及间接父类中声明的权限范围内的属性和方法
  • 子类构造器的第一行默认都是super(),即先调用父类的无参构造器,如果父类中没有无参构造,第一行必须显式的调用某一个有参构造

java.lang.Object类的理解

为什么要单独说Object类,因为所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang. Object类,如果我们没显式的声明一个类的父类的话,则此类继承于java.lang.Object类,通俗点说,这个类就是所有类的祖先,那这个类下的属性和方法其他类都可以使用,即变成了公共的方法。

Object类默认的基本方法有:

  • getClass()
  • hashCode()
  • equals()
  • clone()
  • toString()
  • notify()
  • otifyAll()
  • wait(),
  • inalize()

多态性

何为多态性

父类的引用指向子类的对象(或子类的对象赋给父类的引用),可以理解为一个事物的多种形态。

多态性的使用:虚拟方法调用

Java引用变量有两个类型:编译时类型运行时类型。编译时类型由声明该变量时使用的类型决定,
运行时类型由实际赋给该变量的对象决定。
简 称:编译时,看左边;运行时,看右边

若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)

  • 多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法)
  • “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
//Dog是Animal的子类,最后运行的是子类重写后的方法
Animal animal = new Dog();
public void func(Animal animal){
		animal.eat();
		animal.shout();
	}

多态性使用的注意点

对象的多态性,只适用于方法,不适用于属性,属性编译和运行都看左边

多态性的使用前提

① 类的继承关系

② 方法的重写

关于向上转型与向下转型

  • 向上转型:多态
  • 向下转型:
    有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特有的属性和方法?使用向下转型。

如何实现向下转型

使用强制类型转换符:()

使用时的注意点:

  • 同等级的类,例如兄弟类(有相同的父类),使用强转时,可能出现ClassCastException的异常。不相干的两个类也不能转。
  • 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。

instanceof

a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。

Animal animal = new Dog()
if(animal instanceof Dog)   //true

面试题:

①谈谈你对多态性的理解?

实现代码的通用性。

深入理解:

当父类的引用变量指向子类对象时,被引用的子类对象的类型决定了调用谁的成员方法,引用变量决定可调用的方法。首先会去父类中寻找可调用的方法,找到了就执行子类中覆盖的该方法,就算子类中有现成的方法,同样也会先去父类中寻找,找到后执行重写的子类方法(这里的子类也就是最后决定调用的类方法)

②多态是编译时行为还是运行时行为?

of course 运行时行为

方法的重写

什么是方法的重写

子类继承父类之后,可以对父类同名、同参的方法进行方法体的改写,覆盖父类

为什么会有方法的重写

子类会从父类继承到权限内的属性和方法,且所有子类从父类继承过来的属性和方法都是相同的,重写可以对继承过来的方法进行改写而满足自己的需求,当创建子类对象后,通过子类对象调用重写过的方法时,实际执行的是子类重写的方法。

重写规则

方法的声明:

 权限修饰符  返回值类型  方法名(形参列表) (throws 异常的类型){
 						方法体
				}

约定俗称:子类的叫重写的方法,父类中的叫被重写的方法

  • 子类重写方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同

  • 子类重写的方法的权限修饰符>=父类被重写的方法的权限修饰符

  • 返回值类型:

  • 方法重写时,子类的返回值类型必须要 <= 父类的返回值类型。

  • 方法重写时,子类抛出的异常类型要 <= 父类抛出的异常类型。

  • 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void

  • 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类

  • 父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值

    类型必须是相同的基本数据类型(必须也是double)

  • 特殊情况:子类不能重写父类中声明为private权限的方法,写了虽然不报错,但改写后的方法不构成重写,且调用的函数仍是父类的函数

子类与父类中同名同参数的方法必须同时声明为非static的;当声明为

static时不是重写,因为static方法是属于类的,子类无法覆盖父类的方法。

面试题

重写和重载的区别?

答:其实两个是完全不同的概念,知识名字相近而已。

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;

而对于多态,只等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为

super关键字

super 关键字可以理解为:父类的
可以用来调用的结构:属性、方法、构造器

super调用属性、方法

  • 我们可以在子类的方法或构造器中。通过使用super.属性super.方法的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."

  • 当子类和父类中定义了同名的属性或方法时,我们要想在子类中调用父类中声明的属性,则必须显式的使用super.属性super.方法的方式,表明调用的是父类中声明的属性或方法。

super调用构造器

  • 子类中所有的构造器默认都会访问父类中空参构造器
  • 当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。同时,只能”二选一”,且必须放在构造器的首行
  • 我们可以在子类的构造器中显式的使用super(形参列表)的方式,调用父类中声明的指定的 构造器
  • 在子类构造器的首行,若没显式的声明super(形参列表)this(形参列表)则默认调用的是父类中空参的构造器:super()
  • 如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错
  • 在子类的多个构造器中,至少有一个构造器中使用了super(形参列表)调用父类中的构造器

this vs super

技术分享图片

子类对象实例化过程

此小节理解即可

从结果上看

  • 子类继承父类以后,就获取了父类中声明的属性或方法。
  • 创建子类的对象,在堆空间中,就会加载所父类中声明的属性。

从过程上看
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,……,直到调用了java.lang.Object类中空参的构造器为止。正因为加载了所有父类的结构,子类才可以继承父类的属性或方法

图示

技术分享图片

注意

虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。

技术分享图片

面向对象(中)

原文:https://www.cnblogs.com/silloutte/p/14589626.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!