[Java语法篇2] 继承与多态
一、Java类的继承
1.继承定义与结构
1)继承的定义:
继承是一种基于已有类(被称为父类或派生类)创建新类(被称为子类或派生类) 的一种方式
子类将继承父类的成员变量(包括值)与方法
2)继承的结构
访问权限 class 子类名 extends 父类名{ }
//父类结构
public class Father{
String name;
String mobile;
int age;
//...
}
//子类继承父类
public class Son extends Father{
String address;
//...
}
2.继承的特点
1)Object类是所有类的父类
即使创建类时不加extends Object,创建的类仍是Object的子类
2)Java只有单继承,没有多继承
每个类只能是一个类的子类,不存在一个类是多个类的子类
//Java中不允许存在如下代码,只能单继承
public class Son extends Father1,Father2{
String address;
//...
}
3)一个类如果有final修饰,则该类没有子类,即不能被继承
常见的由final修饰的类:String、System、基本数据类型的包装类(Integer、Byte、Short、Long、Double、Float、Double、Boolean、Character)
//此时父类结构不可被继承
final class Father{
String name;
String mobile;
int age;
//...
}
4)并不是父类所有的方法和成员变量子类都可以继承(private 修饰就不可以)
//此时父类结构不可被继承
public class Father{
String name;
String mobile;
private String hobbit;//此变量不可被子类继承
int age;
//...
}
3)如果子类成员变量“名”和父类相同,则子类对象调用的是子类中的变量;(不考虑数据类型,只考虑名字)
//父类结构
public class Father{
String name = “张氏”;
String mobile;
int age = 42;
//...
}
//子类继承父类
public class Son extends Father{
String address;
String age = "18";
//age变量名与父类的成员变量的变量名同
public static void main(String[] args){
Son son = new Son();
System.out.println(son.name);
//此时输出为父类成员变量name的值"张氏"
System.out.println(son.age);
//此时输出的应为子类成员变量age的值“18”
}
}
二、继承时子类的重写
1.重写的原因
重写即重新改写父类的方法,为了使子类更准确地描述描述子类的行为特征
//父类结构
public class Father{
String name = “张氏”;
String mobile;
int age = 42;
public void showName(){
System.out.println("张氏");
}
public void showRelation(){
System.out.println("This is father");
}
}
//子类继承父类
public class Son extends Father{
String address;
String age = "18";
public void showRelation(){
System.out.println("This is son");
}
public static void main(String[] args){
Son son = new Son();
son.showName();
//此时调用的是子类从父类继承过来且没有重写的方法,输出“张氏”
son.showRelation();
//此时调用的是子类重写后的方法,输出“This is son”
}
}
注:改写后的方法可以使用 @Override注解 修饰(不是注释)
2.重写的注意事项
1)访问权限问题
重写后的方法访问权限要么与父类相同,要么大于父类的访问权限
访问权限大小顺序:public > protected > 默认 > private
private修饰的方法就不能被继承
//父类结构
public class Father{
String name = “张氏”;
String mobile;
int age = 42;
void showRelation(){
System.out.println("This is father");
}
}
//子类继承父类
public class Son extends Father{
String address;
String age = "18";
public void showRelation(){
System.out.println("This is son");
}
//访问权限由 默认 提升为 public
}
2)修饰符问题
父类final修饰的方法,子类不能重写但可以继承
父类中static修饰的方法,子类不能重写但可以继承
子类重写后的方法不能添加static修饰
父类private修饰的方法就不能被继承,所以也不能被重写。
3)重写方法的方法名与参数列表
重写后的方法,方法名必须与父类方法名相同,参数列表除了名字不做限制外,其余必须一样
//父类结构
public class Father{
public int Test(int x, int y, int z){
return x+y+z;
}
}
//子类继承父类
public class Son extends Father{
public int Test(int a, int b, int c){
return a-b+c;
}
//方法名同
//参数列表:参数数目同
// 参数类型同
// 参数名可以不同
}
4)返回值问题
如果 父类方法返回值的数据类型为void或基本数据类型,则 重写后的方法返回值类型必须与父类方法保持一致
如果 父类方法返回值的数据类型为引用类型,则 重写后方法的返回值要么与父类一致,要么是父类方法的返回值的类型的子类
//父类结构
public class Father{
public int Test1(int x){
return x;
}
public Object Test2(Object x){
return x;
}
}
//子类继承父类
public class Son extends Father{
public int Test1(int x){
return x*x;
}
//父类方法返回值为int(基本数据类型),子类重写后必须为int
public String Test2(Object x){
return x;
}
//父类方法返回值为引用类(Object),子类重写后返回值可以为其子类 String
/*
public Object Test2(Object x){
return x;
}
//父类方法返回值为引用类,子类重写后可以保持一致
*/
}
三、Java的多态
1、详述Java多态;2、总结String类常用方法;3、详述父类构造方法对子类构造方法影响;
1.多态的引入
观察如下代码
//哺乳动物类
public class Mammal {
String weight = "2.5KG";
public void move() {
System.out.println("正在移动");
}
}
//蝙蝠类,继承哺乳动物类
public class Bat extends Mammal{
double weight = 2.5;
public void eat() {
System.out.println("进食飞蛾");
}
@Override
public void move() {
System.out.println("靠翅飞行");
}
}
//测试代码
public class Test {
public static void main(String[] args) {
Bat bat = new Bat();
bat.move();
Mammal mammal = new Bat();
mammal.move();//Java多态的表现
}
}
2.Java多态的定义
Java中小范围精度的数据可以直接赋值给大范围数据类型变量,java的不同类间也存在类似的规则
定义:父类类型(比如Mammal)的变量(比如mammal)指向子类创建的对象,使用该变量调用父类中一个被子类重写的方法(比如move方法),则父类中的方法呈现出不同的行为特征,这就是多态。
结构:父类名 变量名 = new 子类名();
//测试代码
public class Test {
public static void main(String[] args) {
Mammal mammal = new Bat();
//父类名 变量名 = new 子类名();
mammal.move();//Java多态的表现,调用的是子类重写后的方法
}
}
3.Bat bat = new Bat();与Mammal mammal = new Bat();创建的对象在编译执行时的区别
以调用类中move()方法为例
前者:bat.move();
编译时:由于变量类型为子类类型,所以编译时调用的子类(Bat)的move方法
运行时:执行时,bat指向的就是子类(Bat)创建的对象,所以调用子类(Bat)重写后的move方法;
后者(多态的表现):mammal.move();
编译时:由于变量类型为父类类型,所以编译时调用的父类(Mammal)的move方法
运行时:执行时,mammal指向的就是子类(Bat)创建的对象,所以调用子类(Bat)重写后的move方法;
4.多态的特点
1)编译时和运行时类型不一致,否则一定不会产生多态
2)编译时调用的方法一定被子类所重写
3)父类类型变量 = 子类创建的对象(又称为上转型对象)
5.上转型对象的特点
1)上转型对象不能调用子类"新增"的属性和方法
//哺乳动物类
public class Mammal {
String weight = "2.5KG";
public void move() {
System.out.println("正在移动");
}
}
//蝙蝠类,继承哺乳动物类
public class Bat extends Mammal{
double weight = 2.5;
public void eat() {
System.out.println("进食飞蛾");
}//子类新增方法,不可被上转型对象调用
@Override
public void move() {
System.out.println("靠翅飞行");
}
}
//测试代码
public class Test {
public static void main(String[] args) {
Bat bat = new Bat();
bat.move();
Mammal mammal = new Bat();
mammal.move();//Java多态的表现
//mammal.eat();//此代码不可用,因为上转型对象mammal不可调用子类新增方法
}
}
注:上转型对象如果必须调用子类新增的属性和方法,则必须下转型
//测试代码
public class Test {
public static void main(String[] args) {
Bat bat = new Bat();
bat.move();
Mammal mammal = new Bat();
mammal.move();//Java多态的表现
Bat b = (Bat) mammal;//对上转型对象进行下转型处理,相当于创建了一个新的变量,但没有创建新的空间
b.eat();//下转型对象调用子类新增方法
Mammal mammals = new Mammal();
//Bat bb = (Bat) mammals; //此代码不正确,因为变量mammals中指向的对象为非上转型对象,不可进行下转型处理
}
}
2)只有上转型对象可以进行下转型操作,非上转型对象不可进行下转型处理
//测试代码
public class Test {
public static void main(String[] args) {
Mammal mammals = new Mammal();
//Bat bb = (Bat) mammals; //此代码不正确,因为变量mammals中指向的对象为非上转型对象,不可进行下转型处理
}
}
3)如果子类成员变量与父类成员变量名字重复(不考虑数据类型),则上转型对象调用的是父类中的成员变量
注:注意区分和继承时的关系,继承后父类变量与子类变量名一致时,正常创建的子类对象调用时调用的是子类的成员变量对应的值,创建的上转型对象则是采用的是父类变量对应的值
//哺乳动物类
public class Mammal {
String weight = "2.5KG";
}
//蝙蝠类,继承哺乳动物类
public class Bat extends Mammal{
double weight = 2.5;
}
public class Test {
public static void main(String[] args) {
Mammal mammal = new Bat();
System.out.println(mammal.weight);//变量指向的时上转型对象,调用的时由于同名,调用父类的成员变量的值
Bat bat = new Bat();
System.out.println(bat.weight);//变量指向的是正常创建的子类对象,调用时由于同名,调用子类成员变量的值
}
}