Java SE Day-08

1、封装

封装概述

1、为什么需要封装?

  • 我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内部的结构吗?有必要碰电动机吗?
  • 我们使用的电脑,内部有CPU、硬盘、键盘、鼠标等等,每一个部件通过某种连接方式一起工作,但是各个部件之间又是独立的
  • 现实生活中,每一个个体与个体之间是有边界的,每一个团体与团体之间是有边界的,而同一个个体、团体内部的信息是互通的,只是对外有所隐瞒。

面向对象编程语言是对客观世界的模拟,客观世界里每一个事物的内部信息都是隐藏在对象内部的,外界无法直接操作和修改,只能通过指定的方式进行访问和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。

随着我们系统越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要遵循“高内聚、低耦合”,而封装就是“高内聚,低耦合”的体现之一:

  • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
  • 低耦合:仅对外暴露少量的方法用于使用

隐藏对象内部的复杂性,只对外公开简单和可控的访问方式,从而提高系统的可扩展性、可维护性。通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

2、如何实现封装呢?

实现封装就是指控制类或成员的可见性范围?这就需要依赖访问控制修饰符,也称为权限修饰符来控制。

权限修饰符:public,protected,缺省,private

修饰符本类本包其他包子类其他包非子类
private×××
缺省××
protected×
public

外部类:public和缺省成员变量、成员方法、构造器

成员内部类:public,protected,缺省,private

3、成员变量/属性私有化问题

成员变量(field)私有化之后,提供标准的get/set方法,我们把这种成员变量也称为属性(property)。

或者可以说只要能通过get/set操作的就是事物的属性,哪怕它没有对应的成员变量。

1、成员变量封装的目的
  • 隐藏类的实现细节
  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入控制逻辑,限制对成员变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。
  • 便于修改,提高代码的可维护性。主要说的是隐藏的部分,在内部修改了,如果其对外可以的访问方式不变的话,外部根本感觉不到它的修改。例如:Java8 -->Java9,String从char[]转为byte[]内部实现,而对外的方法不变,我们使用者根本感觉不到它内部的修改。
2、实现格式
  1. 使用 private 修饰成员变量
private 数据类型 变量名 ;

小结:使用private私有化时,进行一个构造函数的传参和不传承的生成,以及set,与get进行一个后期更改属性的值,达到在实例化对象后,使用的方法时,直接传入参数即可


2、继承

1、继承的概述

在这里插入图片描述

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类中无需再定义这些属性和行为,只需要和抽取出来的类构成某种关系。如图所示:

在这里插入图片描述

其中,多个类可以称为子类,也叫派生类;多个类抽取出来的这个类称为父类超类(superclass)或者基类

继承描述的是事物之间的所属关系,这种关系是:is-a 的关系。例如,图中猫属于动物,狗也属于动物。可见,父类更通用或更一般,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。

2、继承的好处

  • 提高代码的复用性
  • 提高代码的扩展性
  • 表示类与类之间的is-a关系

3、继承的语法格式

通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:

【修饰符】 class 父类 {
	...
}

【修饰符】 class 子类 extends 父类 {
	...
}

4、继承的特点

1.子类会继承父类所有的实例变量和实例方法

从类的定义来看,类是一类具有相同特性的事物的抽象描述。父类是所有子类共同特征的抽象描述。而实例变量和实例方法就是事物的特征,那么父类中声明的实例变量和实例方法代表子类事物也有这个特征。

  • 当子类对象被创建时,在堆中给对象申请内存时,就要看子类和父类都声明了什么实例变量,这些实例变量都要分配内存。
  • 当子类对象调用方法时,编译器会先在子类模板中看该类是否有这个方法,如果没找到,会看它的父类甚至父类的父类是否声明了这个方法,遵循从下往上找的顺序,找到了就停止,一直到根父类都没有找到,就会报编译错误。

所以继承意味着子类的对象除了看子类的类模板还要看父类的类模板。

2、Java只支持单继承,不支持多重继承

public class A{}
class B extends A{}

//一个类只能有一个父类,不可以有多个直接父类。
class C extends B{} 	//ok
class C extends AB...	//error

3、Java支持多层继承(继承体系)

class A{}
class B extends A{}
class C extends B{}

顶层父类是Object类。所有的类默认继承Object,作为父类。

4、一个父类可以同时拥有多个子类

class A{}
class B extends A{}
class D extends A{}
class E extends A{}

5、 权限修饰符限制问题

权限修饰符:public,protected,缺省,private

修饰符本类本包其他包子类其他包非子类
private×××
缺省√(本包子类非子类都可见)××
protected√(本包子类非子类都可见)√(其他包仅限于子类中可见)×
public

外部类:public和缺省

成员变量、成员方法等:public,protected,缺省,private

1、外部类要跨包使用必须是public,否则仅限于本包使用

(1)外部类的权限修饰符如果缺省,本包使用没问题

(2)外部类的权限修饰符如果缺省,跨包使用有问题

2、成员的权限修饰符问题

(1)本包下使用:成员的权限修饰符可以是public、protected、缺省

(2)跨包下使用:要求严格

(3)跨包使用时,如果类的权限修饰符缺省,成员权限修饰符>类的权限修饰符也没有意义

3、父类成员变量私有化(private)

子类虽会继承父类私有(private)的成员变量,但子类不能对继承的私有成员变量直接进行访问,可通过继承的get/set方法进行访问。如图所示:

在这里插入图片描述

小结:在继承中,如果有的类的属性大部分相同,可另外生成一个类进行声明然后让其他类进行一个继承使用。


3、方法重写

我们说父类的所有方法子类都会继承,但是当某个方法被继承到子类之后,子类觉得父类原来的实现不适合于子类,该怎么办呢?我们可以进行方法重写 (Override)

1、IDEA重写方法快捷键:Ctrl + O

在这里插入图片描述

@Override:写在方法上面,用来检测是不是满足重写方法的要求。这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。建议保留,这样编译器可以帮助我们检查格式,另外也可以让阅读源代码的程序员清晰的知道这是一个重写的方法。

2、重写方法的要求

1.必须保证父子类之间重写方法的名称相同。

2.必须保证父子类之间重写方法的参数列表也完全相同。

2.子类方法的返回值类型必须【小于等于】父类方法的返回值类型(小于其实就是是它的子类,例如:Student < Person)。

注意:如果返回值类型是基本数据类型和void,那么必须是相同

3.子类方法的权限必须【大于等于】父类方法的权限修饰符。

注意:public > protected > 缺省 > private

父类私有方法不能重写

跨包的父类缺省的方法也不能重写

3、方法的重载和方法的重写

方法的重载:方法名相同,形参列表不同。不看返回值类型。

方法的重写:见上面。

小结:方法重载就相当于一个方法拥有不同的参数类型,在结构上来说则是一个多个方法同个方法名,方法重写则是说在子类调取父类方法时,子类有自己不同的方法,比如play()方法,Pet父类都有与主人玩的方式,但猫有它自己的方式比如玩毛线球。


4、继承状态下子类调用父类构造方法

  1. 如果子类没有定义构造方法,则调用父类的无参数的构造方法,.

  2. 如果子类定义了构造方法,不论是无参数还是带参数,在创建子类的对象的时候,首先执行父类无参数的构造方法,然后执行自己的构造方法。

  3. 如果子类调用父类带参数的构造方法,可以通过super(参数)调用所需要的父类的构造方法,切该语句做为子类构造方法中的第一条语句。

  4. 如果某个构造方法调用类中的其他的构造方法,则可以用this(参数),切该语句放在构造方法的第一条.

说白了:原则就是,先调用父亲的.(没有就默认调,有了就按有的调,反正只要有一个就可以了.)

子类会从下往上延继承关系依次调用父类的无参构造函数。记住:是无参的构造函数哦。所以,在子类实例化时,不管自己调用的构造函数是有参的还是无参的,父类被调用的构造函数永远是无参的。

package test;

class Father {
    String s = "Run constructor method of Father";

    public Father() {
        System.out.println(s);
    }

    public Father(String str) {
        s = str;
        System.out.println(s);
    }
}

class Son extends Father {
    String s = "Run constructor method of son";

    public Son() {
        //实际上在这里加上super(),和没加是一个样的  
        System.out.println(s);
    }

    public Son(String str) {
        this();//这里调用this()表示调用本类的Son(),因为Son()中有了一个super()了,所以这里不能再加了。  
        s = str;
        System.out.println(s);
    }

    public Son(String str1, String str2) {
        super(str1 + " " + str2);//因为这里已经调用了一个父类的带参数的super("---")了,所以不会再自动调用了无参数的了。  
        s = str1;
        System.out.println(s);
    }
}

public class MyClass9 {
    public static void main(String[] args) {
        Father obfather1 = new Father();
        Father obfather2 = new Father("Hello Father");
        Son obson1 = new Son();
        Son obson2 = new Son("hello son");
        Son obson3 = new Son("hello son", "hello father");

    }
}  
===============
        结果:
        Run constructor method of Father
        Hello Father
        Run constructor method of Father
        Run constructor method of son
        Run constructor method of Father
        Run constructor method of son
        hello son
        hello son hello father
        hello son  

参考

小结:子类调用父类的构造方法说明在子类中可以生成一个新的属性,毕竟不是所有的猫都叫大白,不是所有的猫眼都一样,所以可以在父类的属性的基础上进行一个新的属性的叠加。


5、代码块和加载顺序

1、普通代码块

和构造器一样,也是用于实例变量的初始化等操作。

2、普通代码块的语法格式

【修饰符】 class{
    {
        普通代码块
    }
    【修饰符】 构造器名(){
    	// 实例初始化代码
    }
    【修饰符】 构造器名(参数列表){
        // 实例初始化代码
    }
}

3、静态代码块

如果想要为静态变量初始化,可以直接在静态变量的声明后面直接赋值,也可以使用静态代码块。

1、语法格式

在代码块的前面加static,就是静态代码块。

【修饰符】 class{
	static{
        静态代码块
    }
}

2、静态代码块的特点

每一个类的静态代码块只会执行一次。

静态代码块的执行优先于非静态代码块和构造器。

4、类初始化

a. 类的初始化就是为静态变量初始化。实际上,类初始化的过程时在调用一个()方法,而这个方法是编译器自动生成的。编译器会将如下两部分的所有代码,按顺序合并到类初始化()方法体中。

  • 静态类成员变量的显式赋值语句
  • 静态代码块中的语句

b. 每个类初始化只会进行一次,如果子类初始化时,发现父类没有初始化,那么会先初始化父类。

c. 类的初始化一定优先于实例初始化。

  1. 类初始化代码只执行一次

  2. 父类优先于子类初始化

  3. 类初始化优先于实例初始化

小结:加载顺序一般在初始化阶段,即创建对象时进行(new Object()),加载的顺序是从上往下执行,但是执行时我们的静态代码块会进行一个提升,即先执行。


总结:封装,继承,封装好了便于子类的继承,便于使用。

每日金句:自遣 - 唐代·齐己

了然知是梦, 既觉更何求。 死入孤峰去, 灰飞一烬休。

云无空碧在, 天静月华流。 免有诸徒弟, 时来吊石头。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值