学完哈工大软件构造的一点小心得

本文探讨了Java编程中final关键字的多面应用,包括final修饰引用、方法和类的区别,以及静态方法、实例方法、变量的区分。还涵盖了重写与重载、多态、枚举、接口、泛型、设计模式和SOLID原则等内容,帮助读者理解常见陷阱和最佳实践。

其实这个课应该叫Java编程深入学习(笑

写一点学习时感觉比较容易混淆的点,可能有错漏,大神轻喷

final的修饰作用

用来修饰一个引用

  1. 如果引用为基本数据类型,则该引用为常量,该值无法修改;
  2. 如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改。
  3. 如果引用时类的成员变量,则必须当场赋值,否则编译会报错。(或者在实例化类时,在构造函数将final修饰的成员变量全部赋值)

总之就是固定了引用,使一个引用不能被改变,由于基本数据类型引用和数据是一一对应的,因此固定引用就是固定了数值。

用来修饰一个方法

    当使用final修饰方法时,这个方法将成为最终方法,无法被子类重写。但是,该方法仍然可以被继承。主要用于严格继承,子类无需重写父类中的方法

用来修饰类

      当用final修改类时,该类成为最终类,无法被继承。简称为“断子绝孙类”。

静态方法,静态变量(或者叫类方法,类变量),实例方法,实例变量

概念:实例化,就是你新建一个对象的那个动作就叫实例化,实例方法和变量就是基于已经生成的对象的东西,而类方法和类变量不用实例化变量就可以使用(经典例子:Math.sin()),因为是每个类共有的东西,因此冠以类方法类变量的名称

实例变量(instance variable)

实例变量:或叫实例域、实例字段(instance field),或叫成员变量(member variable)。实例的变量,每个实例的变量可能不同。

实例方法(instance method)

实例方法:或叫成员方法(member method)。供实例用的方法,必须要先有实例,才能通过此实例调用实例方法。

类变量(class variable)

类变量:或叫静态域、静态字段(static field),或叫静态变量(static variable)。出现在这样的情况下:一个类的所有实例需要一个公有的属性,比如,一,统计实例个数;二,常量。类变量与类直接关联在一起。内存当中只有一个地方存放这个变量。任何实例都可以修改它的值(前提是它没有被final修饰符修饰,不然就是常量),但是,访问类变量并不需要实例,用类就可以操作了。

类方法(class method)

类方法(class method):跟类变量的基本特点一样。供类用的方法,可以没有实例,直接通过类来调用类方法。

从形式上看,类变量和类方法,比一般的变量和方法多了一个static修饰符。因为这个原因,类变量和类方法也被叫做静态变量和静态方法。

使用方式

    实例方法可以直接访问实例变量,调用实例方法;

    实例方法可以直接访问类变量,调用类方法。但不推荐这么做,原因是不清晰,容易把类变量误认为是实例变量,把类方法误认为是实例方法(借助IDE,它会给出警告信息);

    类方法可以直接调用类变量和类方法;

    类方法不能直接调用实例变量和实例方法;

    类方法里面不能使用“this”关键字,因为没有实例存在,“this”不知道引用哪个实例。

public, protected,friendly,private

直接上图

作用域

当前类

同一package

子孙类

其他package

public

Y

Y

Y

Y

protected

Y

Y

Y

N

friendly(default)

Y

Y

N

N

private

Y

N

N

N

override&overload

override(重写)发生在子类和父类中。

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

重写规则

参数列表与被重写方法的参数列表必须完全相同。

返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。

访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。

父类的成员方法只能被它的子类重写。

声明为 final 的方法不能被重写。

声明为 static 的方法不能被重写,但是能够被再次声明。

子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。

子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。

重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。

构造方法不能被重写。

如果不能继承一个类,则不能重写该类的方法

overload(重载)可以发生在一个类中,或者父类和子类中

重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

重载规则:

    被重载的方法必须改变参数列表(参数个数或类型不一样);

    被重载的方法可以改变返回类型;

    被重载的方法可以改变访问修饰符;

    被重载的方法可以声明新的或更广的检查异常;

    方法能够在同一个类中或者在一个子类中被重载。

无法以返回值类型作为重载函数的区分标准

重写与重载之间的区别

区别点

重载方法

重写方法

参数列表

必须修改

一定不能修改

返回类型

可以修改

一定不能修改

异常

可以修改

可以减少或删除,一定不能抛出新的或者更广的异常

访问

可以修改

一定不能做更严格的限制(可以降低限制)

多态的三个方面

Ad hoc polymorphism (特殊多态):体现在overload上

Parametric polymorphism (参数化多态):体现在泛型上

subtype polymorphism(子类型多态):体现在继承上

枚举

本质就是类,但是这些类已经实例化并存储在枚举列表中。枚举也可以有自己的构造方法,成员变量和成员方法。具体看https://blog.youkuaiyun.com/qq_35385687/article/details/90147104

接口(静态方法和default方法)

default方法:写在接口中,相当于正常的方法,可以写方法体,也可以正常使用。

作用:由于修改接口需要修改接口所有相关的实现类,非常不利于开发维护,因此使用default给接口添加新的方法和实现,实现类会继承default方法,省去了重复开发的弊端。

静态方法:类似类的静态方法,不用实例化对象就能直接调用。

泛型通配符的一些小知识点

具体看https://www.cnblogs.com/minikobe/p/11547220.html

设计模式(工厂,适配器,装饰器,策略模式,模板模式,迭代器模式,访问者模式)

工厂模式:将创建类的细节和逻辑与客户端隔离开。使用一个接口创建不同的子类。

适配器模式:将一个接口转换为客户需要的另一个接口。

假如客户现在使用的接口是A,而需要B的功能,但是B和A之间不兼容。这时候就可以创造适配器类,通过委托调用B的功能。然后修改A,若需要调用B的功能,则在A中委托生成适配器,间接调用B。

缺点:使用过多类之间的关系会非常混乱,而且一个类只能使用一个适配器

装饰器模式:动态地给一个对象添加一些额外的职责。

假如客户现在使用的接口是A,需要一些功能可以灵活添加,则使用装饰器。首先构造一个抽象装饰类B实现接口A。但是抽象装饰类B并不具体实现装饰,而是委托调用装饰子类实现功能。接着我们写各种装饰子类,都继承自B,每个装饰类内部有具体的装饰功能。同时每个装饰子类中有一个构造函数,参数是最顶层的A。这意味这若要生成拥有多个特征的子类,只需在构造时每层嵌套一个装饰子类即可。

策略模式:没啥好说的,就是全部是委托关系。类要使用的时候可以随意选择不同的策略类进行计算

模板模式:类似给定程序框架,然后框架中的模块可以自定义。具体来说,给一个模板类,里面有一个表示方法逻辑关系的方法,在其中的不同方法的逻辑运行关系和流程是确定的,但是部分具体实现由于情况的不同而不同,这时我们可以通过子类继承该模板类,然后修改实现方法。

注意,表示逻辑和流程的方法应该用final修饰,防止被子类更改。

迭代器模式:以遍历的方式访问集合数据而无需暴露其内部表示,将“遍历”这项功能delegate到外部的iterator对象。

访问者模式:当需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。

具体操作,在被访问的类里面加一个对外提供接待访问者的接口,在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。

在特定ADT上执行某种特定操作,但该操作不在ADT内部实现,而是delegate到独立的visitor对象,客户端可灵活扩展/改变visitor的操作算法,而不影响ADT

LSP原则:

只要父类能出现的地方,子类就可以出现,并且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但反之,未要求。

通俗点:子类可以扩展父类的功能,但不能改变父类原有的功能

具体来说:

  1. 子类可以实现父的抽象方法,但不能覆盖父类的非抽象方法
  2. 覆盖或实现父类的方法时输入参数可以被放大,覆写或实现父类的方法时输出结果可以被缩小
  3. 子类抛出的异常需要比父类具体

实践中:

  1. 在类中调用其它类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已违背了LSP
  2. 如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。
  3. 如果你的程序中出现了if/else之类对子类类型进行判断的条件,则说明类的设计已违背了LSP。

CRP原则:

软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。(能不用继承就不要用继承)

委托(dependance, Composition, Aggregation)

dependance:临时使用,在方法中new一个,使用完成后就抛弃

composition:深入绑定,委派类作为该类的成员。

aggregation:浅绑定,委派类作为该类某个方法的参数进行使用

SOLID编程原则

(SRP) The Single Responsibility Principle 单一责任原则

当需要修改某个类的时候原因有且只有一个。换句话说就是让一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类。 类被修改的几率很大,因此应该专注于单一的功能。如果你把多个功能放在同一个类中,功能之间就形成了关联,改变其中一个功能,有可能中止另一个功能,这时就需要新一轮的测试来避免可能出现的问题,非常耗时耗力。

(OCP) The Open-Closed Principle 开放-封闭原则

软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。这个原则是诸多面向对象编程原则中最抽象、最难理解的一个。

(1)通过增加代码来扩展功能,而不是修改已经存在的代码。

(2)若客户模块和服务模块遵循同一个接口来设计,则客户模块可以不关心服务模块的类型,服务模块可以方便扩展服务(代码)。

(3)OCP支持替换的服务,而不用修改客户模块。

(LSP) The Liskov Substitution Principle Liskov替换原则

客户模块不应关心服务模块的是如何工作的;同样的接口模块之间,可以在不知道服务模块代码的情况下,进行替换。即接口或父类出现的地方,实现接口的类或子类可以代入。

(DIP) The Dependency Inversion Principle 依赖转置原则

(1).高层模块不要依赖低层模块;

(2).高层和低层模块都要依赖于抽象;

(3).抽象不要依赖于具体实现;

(4).具体实现要依赖于抽象;

(5).抽象和接口使模块之间的依赖分离

(ISP) The Interface Segregation Principle 接口聚合原则

不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。

客户模块不应该依赖大的接口,应该裁减为小的接口给客户模块使用,以减少依赖性。如Java中一个类实现多个接口,不同的接口给不用的客户模块使用,而不是提供给客户模块一个大的接口。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值