第五章 继承性、多态
目录
一、继承性(extend)
有一个person类,要新建一个student类,其中student中的一些属性和方法和person里是一样的,这个时候就可以考虑继承,就会获得父类的属性和方法
public class Student extends Person{
// 写自己没定义过就行,person有的student都有,构造器自己写
}
减少代码冗余、提高代码复用性;便于功能的扩展;后面多态的前提
class A extends B {}
A:子类、派生类、subclass
B:超类、基类
1.父类中声明为private的属性和方法,子类继承父类后,仍然认为获取了父类中私有的结构、因为封装性的影响,使得子类不能直接调用父类的结构而已
2.子类继承父类后,还可以声明自己特有的属性和方法,实现功能的扩展。
3.java中对于继承的规定:
一个类可以被多个子类继承
java中类的单继承性:一个类只能有一个父类
子父类是一个相对的概念:多层继承
子类直接继承的父类称为直接父类,间接继承的叫间接父类
子类继承父类吼,就获取了直接父类和所有间接父类包含的属性和方法
4.如果没有显式声明父类,则此类继承于java.lang.Object类
所有的java类(除了他自己)都直接或间接的继承于java.lang.Object
二、方法的重写(overwrite/override)
1.子类中可以根据需要对从父类中继承来的方法进行改造,在执行时,子类的方法将覆盖父类的方法
2.区分的重载和重写:
重载:对于具有相同返回类型,名称的方法,形参不同,构成方法间的重载(两同一不同)
重写:子类对继承而来的父类根据自己需要而重写其中的方法,要求方法的返回类型、名称、形参都相同
3.重写的规定
方法的声明 权限修饰符 返回值类型 方法名(形参列表)throws 异常的类型{
//方法体
}
俗称:子类中的叫重写的方法 父类中叫被重写的方法
①子类重写的方法名和形参类型和父类被重写的方法名he形参列表相同
② 权限修饰符:子类重写方法的权限修饰符不小于父类被重写方法的权限修饰符
特殊:子类不能重写父类中private方法
③返回值类型:
父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型也是void
父类被重写的方法的返回值类型是其他返回值类型的话,子类重写的方法可以是父类返回值类型或者是类型的子类(object和String)
父类被重写的方法的返回值类型是基本数据类型(如:double),则子类重写的方法的返回值类型必须是相同的(如double)
④子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
子类和父类同名同参数的方法 要么都是非static(考虑重写),要么都是static的(不是重写)
三、再讲四类权限修饰符(主要说明protected)
四种不同的权限修饰,这里主要说protected,不同包中的子类是可以看见的
四、super关键字
我在子类中重写了一个方法后,我又想用父类里被重写的方法(有病?)
1.super理解为:父类的
2.super可以用来调用属性、方法、构造器
// 自己的ID 1001
System.out.println(id);
// 父类的ID 1002
System.out.println(super.id);
3.可以在子类的方法或构造器中通过使用“super.属性”或“super.方法”显示调用父类中声明的属性或方法。但是通常情况下会省略"super."
特殊情况:当子类和父类中定义了同名的属性时,我们想要在子类中调用父类中声明的属性必须显示的使用“super.属性”的方法指明调用的是父类的属性
4.方法:我在子类中重写了一个方法后,我又想用父类里被重写的方法,“super.方法”
// 自己的eat 学生多吃有营养的食物
this.eat();
// 父类的eat 吃饭
super.eat();
5.构造器
①我们可以在子类 构造器中显式的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器
②“super(形参列表)”的使用必须声明在子类构造器的首行
③在类的构造其中“this(形参列表)表示使用本类中的其它构造器”或“super(形参列表)表示使用父类中的其他构造器” 只能二选一
public Student (String name, int age,String major){
super(name, age);//这个是父类里面的构造器
this.major = major;
}
④在构造器的首行没有显式的声明“super(形参列表)”或“this(形参列表)”,则默认使用super()构造器
⑤在类的多个构造器中,至少有一个类的构造器中使用了“super(形参列表)”,调用父类中的构造器
五、多态性
可以理解为一个事物的多种形态
对象的多态性:父类的引用指向子类的对象
1.Person p2 = new Man();//Person是Man的父类
p2.eat() 执行的是Man子类的eat方法
就好比队列的定义: Queue que = new LinkedList();
多态的使用:当调用子父类中同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用 即父类可以用子类中重写的方法
但是注意,p2能调的只有Person里的方法,即编译时是编译左边声明的类型,但是执行的时候是执行 Man里面的
3.多态的使用:虚拟方法的调用
有了对象的多态性后,我们在编译期,只能调用父类中声明的方法,在运行期,我们实际执行 的是子类重写的父类的方法
编译,看左边;运行,看右边
4.多态性使用前提:① 要有类的继承关系②方法的重写
5.多态性的好处:避免过多的重载方法(
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
// 如果没有多态性,即使要执行相同的操作,也要写一个新的方法改变形参。
// 因此多态性可以省去重载方法
test.func(new Dog());//输出meat和dog
}
// 如果没有多态性,即使要执行相同的操作,也要写一个新的方法改变形参。
// 因此多态性可以省去重载方法
public void func(Animal animal){ //Animal animal = new Dog();
animal.eat();
animal.shout();
}
}
class Animal{
public void eat(){
System.out.println("eat");
}
public void shout(){
System.out.println("shout");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("meat");
}
public void shout(){
System.out.println("wang");
}
}
6.关于属性:对象的多态性只适用于方法 ,不适用于属性,左边定义的是谁,就是谁的属性
重载不表现为多态性
重写表现为多态性
7.instanceof
有了对象的多态性后,内存中实际上是加载了子类特有的的属性和方法的,但由于声明为父类类型,导致编译时只能调用父类中声明是属性和方法,子类特有的属性和方法不能调用。
如何调用子类特有的属性和方法?(要把对象改成子类的类型)将person类转为man类
Man m1 = (Man)p2;即向下转型(但这么写可能有风险) 下面讲instanceof
使用强转时可能出现ClassCastException异常
instanceof: 使用方法 : a instanceof A判断对象a是否是类A的实例,boolean类型
使用情景:用于向下转型前的判断
如果 a instanceof A 返回TRUE,则 a instanceof B,其中类B是类A的父类
==:对于引用数据类型,是比较两个引用数据类型变量的地址值是否相同
子类重写了父类方法以后,系统不能把父类里的方法转移到子类,但是对于实例变量,子类有和父类同名的实例变量也不可能覆盖父类里定义的实例变量
六、Object类的使用
1.是所有java类的根父类,没有显式声明extends谁的话,就默认是Object。
2.Object类中的功能(属性、方法)
3.Object只声明了一个空参构造器
七、==和equals()的区别
1.==的使用,
==:运算符,用于基本数据类型变量和引用数据类型变量
基本数据类型变量:比较数值(除了Boolean,其他的不计较类型,只比较数值)。不同数据类型比,int i= 10; double j= 10.0; i== j true
char k= 10; i==k true
引用数据类型变量:比较两个对象的地址值是否相同,及两个引用是否指向同一个实体。
2.equals()方法的使用
是一个方法,而非运算符
通过对象.equals()调用,因此无法用于基础数据类型,只能用于引用数据类型
Object类中equals的定义的方法和==的作用是相同的:比较两个对象的地址值是否相同
但是有一些引用变量类型数据,他们的equals是重写过的如:String,Date、File、包装类等都引用数据类型变量了Object()
中的equals, 比较两个对象的“实体内容”是否相同(属性)
通常情况下,自定义的类如果使用equals()的话,可以对其进行重写以实现对于两个对象的实体内容是否相同
equals()可以自动生成,跟get(),set()一样
八、toString()
引用数据类型变量.toString() 输出的是地址值,及当我们输出一个对象的引用时,调用的是object类中的toString()方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
String,Date、File、包装类都重写了toString()。使得在调用对象的toString()方法是,返回“实体内容”的信息(想返回啥你自己写)
九、单元测试方法 包装类
单元测试(JUnit单元测试):可以针对某一段代码进行测试
步骤: 1.build path->add libraries -> JUnit4
2.创建一个Java类进行单元测试。此时的Java类要求:类是公共的;提供一个公共的无参构造器;
3.在此类中声明单元测试方法:此时的单元测试方法:方法的权限是public,没有返回值,没有形参
4.此单元测试方法上需要声明注解:@Test并在单元测试类中导入对应的包:import org.junit.Test
5。声明好单元测试方法以后们就可以在方法体内测试相关代码
6.写完代码后,左键双击单元测试方法名,右键 run as -JUnit Test
说明:执行结果没有任何异常,绿条 否则是红条
包装类(Byte,Short,Integer,Long,Float,Double,Boolean,Character)
面向对象都是针对类来说的,但是基本数据类型并不具备类的特点和功能,所以针对每个基本数据类型,都给他们设计了一个类把他们包起来了
基本数据类型,包装类,string之间的相互转换
1.基本数据类型->包装
2.包装类->基本数据类型
调用包装类XXX的XXXValue()
补充:自动装箱和自动拆箱(不需要用构造器构造包装类了)
自动装箱:
int num1 = 10;
Integer in2 = num1;
自动拆箱:
int num3 = in2;
3.基本数据类型、包装类--->String类型 valueOf
int num = 10;
//方式一:连接运算
String str1 = nums1 + "";
//方式二: 调用String的valueOf(XXX)
float f1 = 12.3f;
String str2 = String.valueOf(f1);//"12.3"
Double d1 = new Double(12.4);
String str3 = String.valueOf(d1);//"12.4"
4.String类型-->基本数据类型包装类 parseXXX
float f1 = 12.3f;
String str2 = Float.parseFloat(f1);//"12.3"
//注意此时的boolean类型有些特殊;
String str2 = "true1";
boolean b1 = Boolean.parseBoolean(str2);
//此时就会报错,布尔类型会严格检查是不是true 或 false,但是不区分大小写
注:对于三元运算符 X? A:B 一般要求A,和B是相同类型,如果类型不同会进行类型提升
Integer内部定义了Integer cache,保存-128~127范围整数,使用自动装箱方式给Integer赋值,在上面这个范围内的可以直接用cache里的数,不需要再new,提高效率