1.继承
什么是继承关系:
可以基于某个父类堆对象的定义加以拓展,而产生新的子类定义,子类可以继承父类原来的某些定义,也可以增加原来父类所没有的定义,或者复写父类的某些特性
从面向对象的角度上说:继承是一种从一般到特殊的关系,是一种”is -a”的关系
在java语言中.存在多个类的时候,我们使用:”extends”关键字来表示子类和父类的关系
语法格式:在定义子类的时候来表明自己需要拓展于哪一个父类
Public class 子类类名 extends父类类名
{
编写自己特有的状态和行为
}
-----------------------------------------------------------------------------------------------------------
在java中,类和类之间的继承关系只允许单继承,不允许多继承
也就是说一个类A ,只能有一个直接的父类.不能出现类A同时继承于类B和类C.但是,java中允许多重继承
多重继承例子:
动物有胎生和卵生,胎生动物有老虎,老虎中有东北虎,华南虎等..
在java中除了object类之外,每个类都有一个直接的父类:
Class student extends Person(){}
Student的直接父类是Person
问题:class Person{}的直接父类是谁:
Object是java语言的根类(老祖宗类,任何类都是object的子类.), Class Person() 等价于 class Personextends object() ,Object要么就是一个类的直接父类,要么就是一个类的间接父类.
继承的作用:
(1)解决代码的重复问题
(2)真正的作用,表达一个体系
先写子类还是先写父类
一般的我们在开发工程中先编写多个自定义类,写完之后,发现多个类之间存在共同的代码,可研抽取一个去做父类,我们以后做开发都是基于框架/组件来做的,我们是在别人的基础上,继续做开发.好比别人提供清水房,我们只需要在清水房的继承之上做装修就可以使用,以后我们定义新的类去继承框架中/组件中提供的父类.
---------------------------------------------------------------------------------------------------------------------------------
子类继承父类之后,可以拥有父类的某一些状态和行为(子类复用父类的功能或状态)
子类继承了父类的哪些成员(根据访问修饰符来判断):
(1)如果父类中的成员使用public修饰,子类无条件继承
(2)如果父类中的成员使用protected 修饰,子类也继承,即使父类和子类不在同一个包中
(3)如果父类和子类在同一个包中,此时子类可以继承父类中的缺省修饰符的成员
(4)如果父类中的成员使用private修饰,子类打死都继承不来,private只能在本类中访问
(5)父类的构造器,子类也不能继承,因为构造器必须和当前的类名相同
2方法覆盖
方法覆写的原则(一同两下一大):
一同
(1)实例的方法前面必须相同*(方法前面= 方法名+ 方法的参数列表)
两小:
(1)子类放到的返回值类型是和父类方法的返回类型相同或者是其子类
(2)子类方法声明抛出的异常类型和父类方法声明抛出的异常类相同或者是其子类
一大:
子类方法的访问权限必须必父类方法访问权限更大或者相等
Private类的方法不能被子类所继承,也就不存在覆盖的概念
判断是否是覆写方法的必杀技:@Override标签;若是覆写方法,则在方法前贴上该标签
编译通过,否则出错;
只有方法存在覆盖的概念,字段没有覆盖
方法覆盖解决的问题:父类的某一个行为不符合子类具体特征的时候,此时子类需要重新定义父类的方法,并且编写方法体
3.方法重载和覆盖的区别
本身二者一点关系都没有,仅仅因为名字很像
1方法重载:overload
作用:解决了同一个类中,相同功能的方法名不同的问题,既然是相同功能,那么方法的名称应该相同只有一个类
规则:两同一不同
2方法重写:override
作用:解决子类继承父类之后,可能父类的某一个方法不满足子类的具体特征.此时需要重新再子类定义中重写方法体
规则:一同.两小,一大
4.super关键字
需求:在子类中的某一个方法中,去调用父类被覆盖的方法
//鸟类
class bird() {
public void fly()
{
System.out.println("fly");
}
}
//企鹅
class Penguin extends bird
{
//重新定义了fly
@Override//当前方法覆盖了父类的方法
public void fly() {
System.out.println("penguin cant fly");
}
public void sing()
{
//调用bird类的fly方法
System.out.println("i am a penguin");
super.fly();
}
}
此时解决方法:使用super关键字,什么是super
This:当前对象,调用this所在的方法,this就是哪一个对象
Super:当前对象的父类对象
5.隐藏和覆盖
子类初始化的过程:创建子类对象的过程
在创建子类对象之前,会先创建父类对象
调用子类构造器之前,在子类构造器中会先调用父类构造器,默认调用的是父类无参数构造器.
(1)如果父类不存在可以被子类访问的构造器,则不能存在子类
(2)如果父类没有提供无参数构造器,此时 子类必须显式通过super语句去调用父类带参数的构造器
Super关键字使用的场景:
(1)可以使用super解决子类隐藏了父类字段的情况
(2)在子类方法中,调用父类被覆盖的方法,引出super的例子
(3)在子类构造器中,调用父类构造器,此时必须使用super语句
所谓隐藏就是”遮蔽”的意思:
(1)如果满足继承的访问权限下,隐藏父类静态方法;若子类定义的静态方法的签名和超类中的静态方法前面相同,那么此时就是隐藏父类方法.注意是:静态方法,子类存在和父类一模一样的静态方法
(2)满足继承访问权限下,隐藏父类字段,若子类定义的字段和超类中的字段名相同(不管类型),此时就是隐藏父类字段,此时只能通过super访问被隐藏的字段
(3)隐藏本类字段:若本类中的某局部变量名和字段名相同,此时就是隐藏本类字段,此时只能通过this访问被隐藏的字段
6.Object
Object要么是一个类的根类,要么就是一个类的间接父类
Class ABC{} 其实等价于 class ABC extends object
------------------------------------------------------------------------------------
所有对象(包括数组)都实现这个类的方法
Object本身指对象的意思,我们发生所有的对象都是具有某一共同行为,所有我们抽象出一个类:object其他对象都会继承于object类,也就用于object类方法
引用数据类型:类/接口/数组,引用类型又称为对象类,所谓的数组名称应该指数组对象
Object类的常见方法:
1)protected void finalize()
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
垃圾回收期在回收某一个对象之前,会先调用该方法,做扫尾工作
2)public final Class<?> getClass()
返回此 Object 的运行时类。返回的Class 对象是由所表示类的static synchronized 方法锁定的对象。
3)public int hashCode()
返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
hashCode决定了对象再哈希表中的存储位置
4)public boolean equals(Object obj)
Equals方法本身和”==”相同,比较对象的内存地址
官方建议:每个类都应该覆盖equals方法,不要比较内存地址,而去比较我们关心的数据.因为关心的是数据而不是内存地址
比如:两个学生对象,我们不管是如何new的,只要学号相同我们就应该任务是同一个对象.
5)public String toString()
返回该对象的字符串表示。
打印对象时,其实打印的就是对象的toString方法
System.out.println(obj对象);等价于System.out.print(obj对象.toString);
默认情况下打印对象,打印的是对象的十六进制hashCode值,但我们更关心对象中村粗的数据
官方建议,覆写toString方法,打印关心的数据
7.多态思想
学了继承关系,我们知道继承关系是一种”is a”关系,也就是说子类是父类的一种特殊情况
问题:子类的对象是动物?
既然子类是一种特殊的父类,那么我们可以不可以任务狗/猫对象时动物类型的对象
-------------------------------------------------------------------------------------------------------------------
当我们的代码变成如下的样子时候,多态就产生了:
Animal a = new Dog();
对象a具有两种类型:
编译类型:声明对象变量的类型,表示吧对象看成什么类型
运行类型:对象的真实类型,Dog,运行类型------>真实类型.
当编译类型和运行类型不同时候,多态就产生了,前提:编译类型是运行类型的父类
所谓多态:对象具有多种形态,对象可以存在不同的形式
Animal a = null;
a = new Dog();//此时a表示DOG类型的形态
a = new Cat();//此时a表示Cat类型的形态
---------------------------------------------------------------------------------------------------------------------------
多态的前提:可以是继承关系(类和类直接),也可以是实现关系(接口和实现类之间).
开发中多态一般指第二种.
-------------------------------------------------------------------------------------------------------------------------
我家里养了一只宠物叫”乖乖”,此时”乖乖”可以有多种形态:
乖乖是狗,乖乖叫声:汪汪
乖乖是喵,乖乖叫声:喵喵
多态特点:
把子类对象赋给父类变量,在运行时期会表现出具体子类的特征(调用子类方法)
8.多态的好处
需求:给适用于提供一个位于动物的方法.用于喂养动物.
没有多态:
针对不同类型的动物,我们得提供不同的喂养方法.不优雅
我只想提供一个方法就能统一喂养所有动物.
存在多态:
统一了喂养动物的行为,
从上述例子可以得知,多态的作用:当把不同子类对象都当做父类类型看待,可以屏蔽不同子类对象之间的实现差异,从而写出统一的代码达到通用编程,以适应需求不断变化.
多态怎么产生.
什么是多态:
多态的特点:
多态的作用
9.多态时方法调用问题
前提:必须先存在多态情况
存在父类:SuperClass ,子类SubClass,方法: doWork;
------------------------------------------------------------------------
测试代码:
SuperClass clz = new SubClass();//多态
Clz.doWork();
情况1:doWork存在于SuperClass ,不存在于SubClass
编译通过,执行SuperClass中的doWork方法.
应该先从SubClass类中去找doWork方法,找不到再去superclass中找
// 父类
class SuperClass {
public void doWork() {
System.out.println("super.dowork");
}
}
// 子类
class SubClass extends SuperClass {
}
// 多态调用方法问题
class MethodCallDemo {
public static void main(String[] args) {
SuperClass clz = new SubClass();
clz.doWork();
}
}
情况2:doWork存在于SubClass ,不存在于SuperClass
编译错误
编译时期,回去编译类型(SuperClass)中寻找是否有dowork方法
找到:编译通过
找不到:编译错误
// 父类
class SuperClass {
}
// 子类
class SubClass extends SuperClass {
public void doWork()
{
System.out.println("sub.dowork");
}
}
// 多态调用方法问题
class MethodCallDemo {
public static void main(String[] args) {
SuperClass clz = new SubClass();
clz.doWork();
}
}
情况3:doWork存在于SubClass ,也存在于SuperClass
此时编译通过: 执行SubClass中的doWork方法
运行时期,调用运行类型中的(Subclass)中的方法
// 父类
class SuperClass {
public void doWork() {
System.out.println("super.dowork");
}
}
// 子类
class SubClass extends SuperClass {
public void doWork()
{
System.out.println("sub.dowork");
}
}
// 多态调用方法问题
class MethodCallDemo {
public static void main(String[] args) {
SuperClass clz = new SubClass();
clz.doWork();
}
}
情况4:doWork存在于SubClass ,也存在于SuperClass,但是doWork是静态方法
// 父类
class SuperClass {
public void doWork() {
System.out.println("super.dowork");
}
}
// 子类
class SubClass extends SuperClass {
public static void doWork()
{
System.out.println("sub.dowork");
}
}
// 多态调用方法问题
class MethodCallDemo {
public static void main(String[] args) {
SuperClass clz = new SubClass();
clz.doWork();
}
}
此时这种情况,我们称之为隐藏,而不叫方法覆盖
此时结果:编译通过,执行superclass中的doWork方法
静态方法的调用只需要类就可以;
如果使用对象来调用静态方法,其实使用的是对象的编译类型来调用静态方法
10.引用类型的转换:
引用类型的大和小,指的是父类和子类的关系
自动类型转换:把子类对象赋给父类变量;
Animal a = new Dog();
强制类型转换 把父类类型对象赋给子类类型变量(但是该父类类型变量的真实类型应该是子类类型)
Animal a = new Dog();
Dog b = (Dog) a;
Instanceof 运算符
判断该对象是否是某一个类的实例
语法格式: boolean b = 对象 A instanceof 对象B
若对象是类的实例返回true
若对象是类的父类实例,也返回true
---------------------------------------------------------------------------------------
在开发中,有时候我们希望判断是真实类型的实例,而不想判断为编译类型的实例
System.out.println(Obj instanceof Object );//true
System.out.println(Obj instanceof String);//true
---------------------------------------------------------------
System.out.println(obj.getClass());//获取对象的真实类型
继承关系:子类可以继承到父类中的部分成员,那么此时子类是可以修改父类的信息
继承关系破坏封装
为什么引入继承: 为了代码复用问题
解决代码复用问题,不一定非要使用继承,也可以使用”包含关系”(has a)
通过在各自的类中创建 t的对象,然后调用t的方法,这样有了组合
如果A类为了得到B类的功能行为:
如果a是b的一种特殊情况,我们就应该使用继承
否则采用组合
说明:本文乃学习整理参考而来.