java 继承和多态
基础语法
利用extends来进行继承,例如:
public class Sub extends Base |
以上表明Sub类继承Base类,具体继承还要分两种情况。
- 如果在同一个包中,那么Sub继承了Base中的public,protected和默认访问级别的成员变量和方法
- 如果在不同的包中,不继承默认访问级别的成员变量和成员方法,其他和上一个相同。
默认访问级别就是前面没有加任何关键字。
java不支持多继承,一个类只能继承于一个类,但是父类可以有多个子类,就像一棵树。下级继承会继承同一分支上所有的成员和方法。
java中有一个所有类的祖先叫object类,如果没有使用extends关键字那么会自动继承这个类。
方法覆盖
覆盖指的是父类中的函数在子类中重新写。要满足下列条件:
- 子类方法名称,函数签名(包括参数位置)和返回类型都要和父类方法一致
- 在子类中,必须要先覆盖函数,才可以重写函数
Base类中: |
- 子类方法不可以缩小父类的访问权限,如果父类的方法时private那么子类就不能写public
- 子类不能抛出比父类更多的异常
- 子类无法覆盖父类的静态方法,只能够隐藏。这两者的区别是覆盖的话父类可以使用子类的,子类也可以使用父类的。隐藏的话父类只能使用父类的,子类只能使用子类的。这是由于static定义的区域所决定的。
例:
package newpackage; |
- 可以扩大访问权限。并且如果是抽象类,子类可以只扩大访问范围而不做任何事.但是必须要实现它,不然只能让子类也定义成抽象类。
例如:
父类定义类A()的抽象方法,子类可以这样 |
super
super和this一样都是指向一个对象,不同的是super指向父类,this指向自己。
使用场合:
- 局部变量和类变量重名
- 子类覆盖了父类的方法或成员变量时,可以用这种方法使用父类的方法
需要注意,如果父类成员变量和方法被定义成private,那么子类无论如何都无法访问他们。不能再静态方法区使用super关键字。
此外如果在父类第一行使用super()是调用父类构造函数
多态
多态就是父类可以使用子类,子类也可以使用父类(自我理解)。下面讲具体使用规范。
儿子可以用爸爸一切公有的,而爸爸如果引用了自己的实例就不能用儿子的,只有引用了儿子的实例才可以使用儿子继承于爸爸的。
- 对于一个引用类型变量,编译器按声明变量进行处理。例如
Base who = new Sub(); |
- 编译器允许继承分支关系的类进行类型转换。对于向上转型(子类使用父类的),编译器会自动类型转换。对于向下转型则需要强制类型转换。在运行时,子类可以转换成父类,但是父类实际上无法转换成子类。因为父类有的子类一定有,子类有的父类却不一定有。
对于引用对象绑定,遵循如下规则
- 对于实例方法和引用变量是由实际使用的对象进行绑定。这个是动态的,也就是说方法的实际输出是由实际类型所决定。例如 Base a = new Sub();输出时Sub的。如果这个时候Sub的子类b = a;那么输出还是Sub的。但是如果使用了子类中独有的方法会报错
- 静态方法和成员变量由前面定义的类型所决定,这属于静态绑定,在编译时期已经决定。无论引用了谁都不会改变。
继承的使用
继承树的最上层一般是抽象层。并且继承树上有接口类型,那么尽可能的把应用变量声明成继承树上层的接口类型。因为实例可以是子类,向上转型不会出现问题,这样便于使用多个子类,如果想用子类独有的东西就把引用变量定义成子类。
继承关系会打破封装,因为上层类改变会使下层类也跟着改变。所以对上层类定义要谨慎。
修饰符
修饰符 | 类 | 成员方法 | 构造方法 | 成员变量 | 局部变量 |
---|---|---|---|---|---|
abstract(抽象的) | 有 | 有 | 无 | 无 | 无 |
static(静态的) | 无 | 有 | 无 | 有 | 无 |
public | 有 | 有 | 有 | 有 | 无 |
protected | 无 | 有 | 有 | 有 | 无 |
private | 无 | 有 | 有 | 有 | 无 |
synchronized(同步的) | 无 | 有 | 无 | 无 | 无 |
native(本地的) | 无 | 有 | 无 | 无 | 无 |
transient(暂时的) | 无 | 无 | 无 | 有 | 无 |
volatile(易失的) | 无 | 无 | 无 | 有 | 无 |
final(不可改变的) | 有 | 有 | 无 | 有 | 有 |
表中的类指的是顶层类,与内部类相对应(内部类是定义在类或方法中的类)。
访问控制修饰符
访问控制指的是其他类或对象可以查看的东西。一共有四种访问级别
- public 所有都是可以给外部看的
- protected 只对子类和一个包中的类公开
- 默认级别 没有修饰符 只对同一个包中的类公开,不对子类公开
- private 不对外公开
public>protected>默认>private。public与其他不同是其他包也可以查看
顶层类只能是public或protected,不然会出现编译错误。
访问级别是对成员变量和类来说的,对局部变量没有意义,局部变量的访问范围就是方法内部。
abstract
用abstract修饰的类表示抽象类,抽象类不允许实例化,也就是抽象类不允许创建实例
用abstract修饰的方法表示抽象方法,抽象方法内部没有内容,只有一个函数头。
注意,如果有抽象方法,这个类必须定义成抽象类。此外,没有抽象静态方法,abstract和static是互相矛盾的。
抽象类本身不可以实例化,但是可以创建一个抽象类的引用变量然后引用具体类。
Base a = new Base();//不可以,因为Base是抽象类 |
抽象方法不可以用private修饰,因为抽象出来本来就是要让子类去实现的,private一下子类就无法使用了。
子类必须实现抽象类的所有抽象方法。
final
final有不可改变的含义。
用final修饰的类不能被继承,没有子类
用final修饰的方法不能被覆盖
final修饰的变量表示常量,只能赋一次值
final不能用来修饰构造方法。
final修饰引用变量,这个变量不能只能引用一个实例,但是可以对这个实例进行修改。
不允许覆盖就是不能用相同的函数签名去重写这个函数
static
- static修饰的成员变量时静态变量,可以直接通过类名进行访问,并且所有实例共用一个静态变量
- 修饰成员方法表示静态方法,可以直接通过类名访问
- 修饰的程序代码块叫做静态代码块,加载类时会执行这些代码块
静态变量只有一份备份,处于方法区,在加载类的时候随之加载。
注意,在静态方法中没办法用this,因为静态方法时所有类共有的,this表示的是某个特定的类。所以在main方法中不能用this和super,因为main是static的,如果想访问这个类的内容,可以先定义一个该类的引用变量。
如果直接访问了,编译器会报在静态方法内不允许访问非静态变量。
main函数必须是static的原因是只要加载了main所在的类main就被加载了,可以直接去执行,不然还要先创建一个类然后使用main方法。
类中可以包含一些静态代码块,这些代码块不存在与任何方法中,加载类时一次执行这些代码块。例如:
public class sample |
static代码块和静态方法一样,不可以直接调用实例变量和实例方法,只用通过先引用之后才可以进行调用。
static进行静态导入
静态导入类似于c++中的using namespace std;可以简写代码。例如
import static java.lang.Integer.MIN_VALUE; |
但是如果静态导入过多,可能会导致冲突,因此尽量导入的时候具体一点。