详细理解JAVA面向对象的封装,继承,多态,抽象

时间:2021-10-12 00:14:20

创建类的对象 = 类的实例化 = 实例化类

类和对象的使用(面向对象思想落地的实现):

1.创建类,设计类的成员

2.创建类的对象

3.通过“对象.属性”或“对象.方法”调用对象的结构

如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)

意味着,如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。

子类对象实例化的全过程

1.从结果上看:(继承性)

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

2.从过程上来看:

当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器。直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类的结构,子类对象才可以考虑进行调用。

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

匿名对象

1. 理解:我们创建的对象,没有显式的赋给一个变量名,即为匿名对象

?
1
2
3
4
5
6
class Phone{
    Phone p = new phone();//普通方式创建对象
    p.sendEmail();
    new Phone().sendEmail();//匿名对象只能直接调用方法
    new Phone().price = 1999;//匿名对象调用属性
}

2.特征:匿名对象只能使用一次

3.匿名对象在开发中的使用

?
1
2
3
4
5
6
7
8
9
10
11
12
class Phone{
    public static void main(String[] args){
        PhoneMall mall = new PhoneMall();
        //匿名对象的使用
        mall.show(new Phone());
    }
}
class PhoneMall{
    public void show(Phone phone){
        phone.sendEmail();
    }
}

1.封装性

封装性的体现:

我们将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置属性

拓展:封装性的体现:A. 如上 B.不对外暴露的私有的方法 C.单例模式.....

封装性的体现,需要权限修饰符来配合。

1. Java规定的4中权限(从小到大排列):private、缺省、protected、public

2. 4种权限修饰符可以用来修饰类的内部结构:属性、方法、构造器、内部类

3. 修饰类的话,只能使用:缺省、public; public类可以在任意地方被访问。

default类只可以被同一个包内部的类访问。

2.继承性

继承性的格式:class A extends B{}

A:子类、派生类、subclass

B:父类、超类、基类、superclass

体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。

特别的,父类中声明为private的属性和方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只是由于封装性的影响,使得子类不能直接调用父类的结构而已。

子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的扩展。子类和父类的关系,不同于子集和集合的关系。

Java中关于继承性的规定:

1.一个类可以被多个子类继承。

2.Java中类的单继承性,一个类只能有一个父类

3.子父类是相对的概念。

4.之类直接继承的父类,称为:直接父类;间接继承的父类称为:间接父类

5.子类继承父类以后,就获取了直接父类以及所有间接父类中生命的属性和方法

----------

1.如果我们没有显示的声明一个列的父类的话,则此类继承于java.lang.Object类

2.所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类

3.意味着,所有的java类具有java.lang.Object类声明的功能。

继承性的好处:

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

在使用子类实例时,如果我们想要使用某些父类的属性或方法,可以借助构造器和封装方法;如下:

?
1
2
3
4
5
6
7
8
public class Father{
    //父类中的私有的属性
    private double ownMoney = 2000;//私房钱
    //父类中的受保护的属性
    protected double money = 5000;
    //父类中的公开属性
    public String name = "老张";
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Son extends Father{
    //子类中的独有属性
    ...
    //使用构造器为属性赋值
    public Son(String name,double money){
        super.name = name;
        super.money = money;
    }
    //使用封装方法操作父类中的属性
    public void setMoney(double money){
        super.money = money;
    }
    public double getMoney(){
    return super.money;
    }
}
?
1
2
3
4
5
6
7
8
9
10
public class Test{
    public static void main(String[] args){
        //在test包中的Test类中创建Son实例
        Son son = new Son("小张"3000);//为父类继承而来的属性赋值
        //以下代码编译通过
        double money = son.getMoney();
        System.out.println(money);
        son.setMoney(money - 500);
    }
}

从以上的例子看到:测试类对于字符类来说是一个处在不同包中的完全无关的类,在调用时会被权限修饰符所限制,所以这里明确一下:权限修饰符是根据类的搜在路径 与列之间的结构关系惊醒限定的,不是说在任意一个地方使用子类实例都能调用出父类中的属性和方法。

3.多态性

1.理解多态性:可以理解为一个事物的多种形态。

2.何为多态性:

  • 对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)

3.多态的使用:虚拟方法调用

  • 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。

总结:编译,看左边;运行,看右边。

4.多态性的使用前提:A.存在子父类继承关系 B.子类重写父类的方法 C.父类引用指向子类对象

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

5.1 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中,编译看左边,运行看右边。

5.2 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量,编译运行都看左边。

6.多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法----->虚拟方法调用

虚拟方法调用

正常的方法调用:

?
1
2
3
4
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();

虚拟方法调用(多态情况下)

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法,这样的方法调用在编译期是无法确定的。

?
1
2
Person e = new Student();
e.getInfo();//调用Student类的getInfo()方法

编译时类型和运行时类型

编译时e为Person类型,而方法的调用时在运行时确定的,所以调用的是Student类的getInfo()方法。------动态绑定

?
1
2
3
4
5
6
7
8
9
public class PersonTest{
    public static void main(String[] args){
        Person p2 = new Man();
        Person p3 = new Woman();
        p2.name = "Tom";
        //不能调用子类所特有的方法、属性;编译时,p2是Person类型。
//      p2.earnMoney();//earnMoney()是子类Man()的方法
    }
}

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

如何才能调用子类特有的属性和方法?

答:向下转型,使用强制类型转换符。

?
1
2
3
4
5
Man m1 = (Man)p2;
m1.earnMoney();
//使用强转时,可能出现ClassCastException的异常
Woman w1 = (Woman)p2;//p2是Man;两者并列关系
w1.goShopping();

解决这个问题的办法是先使用关键字(instanceof)判断一下!!!!! (会在下一篇博客关键字中介绍)

4.抽象性

背景:有两个在逻辑上看似相关的类,我们想要把他们联系起来,因为这样做可以提高效率,例如:矩形、圆形,都可以具有周长和面积两个方法,但是计算的方式完全不同,矩形和圆形之间肯定不能构成子父类的关系,那么只能是同时去继承一个父类。这时,就引出了抽象的概念。

1.抽象类的特点:

抽象类的本质依然是一个类,所以具备着一个普通类的所有功能,包括构造方法等的定义,总结一下,抽象类具有以下的几个特点:

  • A. 抽象类由abstract修饰
  • B. 抽象类中允许出现抽象方法
  • C.抽象了不能通过构造器直接实例化
  • D.可以在抽象类中定义普通方法供子类继承。
?
1
2
3
4
5
public abstract class Figure{
    //定义计算周长的抽象方法:getC()
    //抽象方法,不能有方法主体
    public abstract double getC();
}

2.天生的父类:抽象类

2.1 抽象类不能直接实例化(编译无法通过),是天生的父类

2.2 如果一个类继承了抽象类,那么必须重写父类中的抽象方法

2.3 如果抽象类中定义了构造方法,可以被子类调用或在实例化子类对象时执行

2.4 如果抽象类的子类依然是抽象类(需要用abstract声明),可以不重写抽象方法,将重写操作留给下一级子类

2.5 抽象类不能使用final关键字修饰,因为final 修饰的类是无法被继承

2.6 抽象类的匿名子类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PersonTest{
    public static void main(String[] args){
        method(new Student());//匿名对象
        Worker worker = new Worker();
        method1(worker);//非匿名的类非匿名的对象
        method1(new Worker());//非匿名的类匿名的对象
        //创建了一匿名子类的对象:p
        Person p = new Person(){
            @Override
            public void eat(){
            }
        }
        //创建匿名子类的匿名对象
        method1(new Person()){
            @Override
            public void eat(){
            }
        }       
    }
}
class Worker extends Person{
}

3.抽象方法

3.1 抽象类中的抽象方法只是声明,不包含方法体

3.2 抽象方法不能用private修饰,因为抽象方法必须被子类实现(重写),而private 权限对于子类来说是不能访问的

3.3 一个类继承了一个抽象类,那么它必须全部重写抽象类中的抽象方法,当然也可以不全部重写,如果不重写全部抽 象方法则这个子类也必须是抽象类

3.4 构造方法,类方法(static修饰的方法)不能声明为抽象方法

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注服务器之家的更多内容!

原文链接:https://blog.csdn.net/weixin_49329785/article/details/118881231