栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

阻止继承以及抽象类

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

阻止继承以及抽象类

阻止继承以及抽象类
  • 阻止继承:final类和方法
  • 强制类型转换
  • 抽象类

阻止继承:final类和方法

有时候,我们可能希望阻止人们利用某个类定义子类。不允许扩展的类叫做final类。如果在定义类的时候使用了final修饰符就表示这个类是final类。例如,假设希望阻止人们派生Executive类的子类,就可以在声明这个类的时候使用修饰符。 声明格式如下所示:

public final class Executive extends Manager
{
....
}

类中的某个特定方法也可以被声明为final。如果这样做,子类就不能覆盖这个方法。例如:

public class Employee
{
...
	public final String getName()
	{
	return name;
	}
}

将方法或者类声明为final类的主要原因是:确保他们在子类中不会改变语义。例如Calender类与getTime和setTime方法都声明final。这表明Calender类的设计者负责实现Date类与日历状态之间的转换,而不允许子类来添乱。同样的,String类也是final类,这是意味着不允许任何人定义String的子类。换言之,如果有一个String引用,它引用的一定是一个String对象,而不可能是其他类的对象。

强制类型转换

我们知道,将一个类型强制转化为另一个类型的过程叫做强制类型转化。Java语言为强制类型转换提供了一种特殊表示法:

double x = 3.405;
int nx = int(x);

将表达式x的值强行转化为整数类型,去掉了小数部分。
正像有时候需要将浮点数转化为整数一样,有时候也可能将某个类的对象引用转化成另一个类的对像引用。要完成对象引用的强制类型转换,转换语法与数值表达式的强制类型转换类似,仅仅需要将目标对象用圆括号括起来,并放置在需要转换的对象引用之前就可以了。例如:

Manager boss = (Manager) staff[0];

进行强制类型转换的唯一原因是:要在暂时忽视对象的实际类型之后使用对象的全部功能。例如在managerTest类中,由于某些元素是普通员工,所以staff数组必须是Employee对象的数组。我们需要将数组中引用经理的元素复原成一个Manager对象,以便能够访问新增加的所有变量。
我们都知道Java中,每个对象都必须有一个类型。类型描述了这个变量所引用的以及能够引用的对象类型。例如,staff[i]引用一个Employee对象。
将一个值存入变量时,编译器将检查你是否承诺过多。如果将一个子类的引用赋给一个超类变量,编译器是允许的。但将一个超类变量赋予子类时,就承诺过多了。必须进行强制类型转换,这样才能通过检查。
如果试图在继承链上进行向下的强制类型转换,并“谎报”对象包含的内容,会发生什么呢?

Manager boss = (Manager) staff[1];//错误

运行这个程序时,Java运行时系统将注意到你的承诺不符,并产生一个ClassCastException异常。如果没有捕获这个异常,那么程序就会终止。因此,应该养成这样一个良好的程序设计习惯:要进行强制转换之前,先查看是否能成功转换。为此只需要使用instanceof操作符就能实现。例如:

if (staff[1] instanceof Manager)
{
	boss = (Manager) staff[1];
	...
}

最后,如果这个类型转换不肯成功,编译器就不会让你完成这个转换。
综上所述:

  • 只能在继承层次内进行强制类型转换。
  • 在将超类强制转化为子类之前,应该使用instanceof进行检查。

实际上,通过强制转化来转化对象类型不是一个好方法。在我们的程序中,大多数情况不需要将Employee对象强制转化为Manager对象,而两个类的对象都能正确调用getSalary方法,这是因为实现多态性的动态绑定机制能够自动的找到正确的方法。
只有在使用Manager类中的特有方法才需要进行强制转换。但是重新设计超类方法才是合适的方法

抽象类

如果自下而上在类的继承层次结构中上移,位于更上层的类更具有一般性,可能更加抽象。从某种角度来看,祖先类更具有一般性,人们只将它作为派生其他类的基类,而不是用来制造你想实用的特例的对象。例如考虑拓展Employee类的层次结构。员工是一个人,学生也是一个人。下面扩展我们的类层次结构来加入人类Person和类Student。
为什么要那么麻烦提供这样一个高层次的抽象呢?每个人都有一些属性,例如姓名。学生跟员工都有姓名属性,因此通过引入一个公共的超类,我们就可以把getName方法放在继承层次结构中更高的一层。
现在,在增加一个getDescription方法,它可以返回一个人的简短描述。例如:

一个员工,工资5000
一个学生,计算机专业

在Employee类和Student类中实现这个方法很容易。但是在Person类中应该提供什么内容呢?除了姓名之外,Person类对这个人一无所知。当然可以让Person.getDescription()返回一个空字符串。不过还有一个更好的方法,就是使用abstract关键字,这样就完全不需要实现这个方法了。
为了提高程序的清晰度,包含一个或多个抽象方法的类本身必须被声明为抽象的。

pubilc abstract class Person
{
	...
	public abstract String getDescription();
}

除了抽象方法之外,抽象类还可以包含字段和具体方法。例如,Person类还保存着一个人的姓名,另外有一个返回姓名的具体方法。

public abstract class Person
{
	private String name;
	public Person(String name)
	{
		this.name=name;
	}
	
	public abstract String getDescription();
	
	public String getName()
	{
		return name;
	}
}

抽象方法充当着占位方法的角色,它们在子类中的具体实现。扩展抽象类可以有两种选择。一种是在子类中保留抽象类中的部分或所有抽象方法仍未定义,这样就必须将子类也定义为抽象类;另一种是定义所有抽象类中的方法,这样一来,子类就不是抽象类了。
例如,通过扩展抽象类Person类,并实现getDescription方法来定义Student类。由于Student类中不在含有抽象方法,所以不需要声明这个类为抽象类。
即使不含抽象方法,也可以将类声明为抽象类。
抽象类不能实例化。也就是说,如果将一个类声明为abstract,就不能创造这个类的对象。例如表达式new Person("Vince vu") 是错误的,但可以创建一个具体子类的对象。
需要注意的是,可以定义一个抽象类的对象变量,但是这样一个变量只能引用非抽象子类的对象。例如:

Person p = new Student("Vince vu","Economics");

这里的p是一个抽象类型是Person的变量,他应用了一个非抽象子类Student的实例。
下面定义一个扩展抽象类Person的具体子类Student:

public class Student extends Person
{
	private String major;
	public Student (String name,String major)
	{
		super(name);
		this.major=major;
	}
	public String getDescription()
	{
		return "a student majoring in"+ major;
	}
}

Student类定义了getDescription()方法。因此在Student类中全部的方法都是具体的,这个类不再是抽象类。
程序清单4的程序定义了抽象超类Person(见程序清单5)和两个具体子类Employee类(程序清单6)和Student类(程序清单7)
在Java中抽象是一个重要概念。接口会有更多的抽象方法,之后会详细讲解。

程序清单4

package 抽象继承类;

public class PersonTest {
    public static void main(String[] args) {
        var people = new Person[2];

        people[0] = new Employee( "Harry Hacker", 50000, 1990, 10, 1);
        people[1] = new Student ("Water Corleone","computer science");

        for(Person p:people){
            System.out.println(p.getName()+","+p.getDescription());
        }
    }
}

程序清单5

package 抽象继承类;

public abstract class Person {
    private String name;
    public Person(String name)
    {
        this.name=name;
    }

    public abstract String getDescription();

    public String getName()
    {
        return name;
    }
}

程序清单6

package 抽象继承类;
import java.time.*;
public class Employee extends Person{
        private double salary;
        private LocalDate hireDay;

        public Employee(String name,double salary,int year,int month,int day){
            super(name);
            this.salary = salary;
            hireDay = LocalDate.of(year,month,day);
        }

        public double getSalary()
        {
            return salary;
        }
        public LocalDate getHireDay()
        {
            return hireDay;
        }

    public String getDescription() {
        return String.format("an employee with a salary of $%.2f",salary);
    }

    public void raiseSalary(double byPercent){
            double raise =salary*byPercent/100;
            salary+= raise;
        }
}

程序清单7

package 抽象继承类;

public class Student extends Person
{
    private String major;
    public Student (String name,String major)
    {
        super(name);
        this.major=major;
    }
    public String getDescription()
    {
        return "a student majoring in"+ major;
    }
}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/872018.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号