首先为什么会有抽象类和接口的产生?
当提到这里时必须先讲一讲方法覆写
方法覆写:定义了方法名称,返回值类型,参数的类型及个数完全相同,
被覆写的方法不能有比子类更为严格的访问控制权限。
方法覆写:多态的关键所在。
所以为了强制要求子类必须覆写父类方法产生了抽象类与接口.
多态:一个类实例的相同方法在不同情形下有不同的表现形式。
回顾:
向上转型:参数统一化
父类 父类引用 = new 子类实例();
father per = new Student();
向下转型:要发生向下转型必须先发生向上转型
子类 子类引用 = (子类) 父类实例;
Student stu = (Student) per;
所以不管是向上转型还是向下转型都发生了方法覆写。
抽象类说明符abstract:
一个类被声明为abstract,则表示该类是无具体对象的抽象类,
即该类不能进行实例化操作.不能用new关键字创建该类的对象,
只能由其子类的抽象方法的具体实现来完成.
抽象类:
Abstract:
定义:抽象类是普通类的超级,只是比普通类多了一些抽象方法而已。
首先知道:
抽象方法:使用抽象方法定义的只有方法声明没有方法体的方法.
语法:
public abstract void test();
//只有方法声明,没有方法体的方法
- 抽象类中包含抽象方法,则抽象类也必须使用abstract来定义,表示抽象类
本地方法(Java调用c同名方法):使用native定义的只有方法声明没有方法体的方法。
public native int hashCode();
注意:只有方法声明没有方法体的方法就是抽象方法(×)
因为在Java中还有native(本地方法);
抽象类的使用限制或者使用原则
(抽象类一定不能new)
-
a.所有抽象类必须有子类(final与abstract不能同时出现)
原因:abstract一定有子类,而final一定没有子类(不能继承)。 (final可以修饰类)
因为由子类的抽象方法的具体实现来完成。
例子:
//具体定义
abstract class Person{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
//只声明而为实现的方法(不能直接使用,只能由子类的抽象方法来实现完成)
public abstract void getPerson();
}
public class Test{
public static void main(String[] args) {
}
}
- b.抽象类的子类必须覆写所有抽象方法(或者子类也使用abstract关键字定义)
定义抽象类的目的就是想让类继承,实现覆写
private与abstract不能同时出现,抽象方法必须被覆写而private方法无法被覆写
- c.抽象类方法无法直接产生实例化对象,但可以通过子类向上转型进行实例化。
(解释:抽象类是“半成品”,不能直接new 自己;由于存在向上转型,如果子类是一个普通类,可以把他变为抽象类进行使用)
且子类依然遵循对象实例化流程,先调用抽象类构造方法而后再调用子类构造。
(解释:先调用父类构造,产生父类对象,才有子类对象)
(抽象类是普通类的超级,普通类有构造方法,那抽象类一定有构造方法)
- d.抽象类可以没有抽象方法,但是此时任然不能直接实例化对象。
例子:
//具体定义
abstract class Person{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
//只声明而为实现的方法(不能直接使用)
public abstract void getPersonInfo();
}
class Student extends Person{
public void getPersonInfo(){
System.out.println("111");
}
}
public class Test{
public static void main(String[] args) {
//通过向上转型,把Student对象指向了一个父类的引用
Person per = new Student();
//这时调用per.getPersonInfo();执行输出
per.getPersonInfo();
}
}
笔试题:
//请问输出是多少?
A.0
B.30
C.100
D.编译错误
abstract class Person {
public Person() {
this.print();
}
public abstract void print();
}
class Student extends Person{
private int num = 100;
public Student(int num){
this.num = num;
}
public void print(){
System.out.println(num);
}
}
public class Test{
public static void main(String[] args) {
new Student(30);
}
}
输出:A.0
解释:
一:**从入口开始看**,
第一步调用子类Student这个类的构造方法,将30传值,但是
在**子类构造中有一个隐藏的super();(因为继承关系)**,所以先调用
父类构造方法再到子类。故当调用子类这个有参构造时连方法都进不去。
二:所以先在父类(**本质上第一步从父类开始**),Person的无参构造
调用了print方法(this表示本类对象),this在父类中,且用abstract关键字修饰,
所以此时print方法调用不了。
三:故去寻找子类中有无print方法,至子类中的print方法中,
子类的print方法覆写了父类,故从子类的覆写后的方法进行输出,
此时的num还未进行this.num = num;这一行的赋值。
四:实际上private int num = 100;这一行在编译的时候在构造方法中执行,
但是在主方法中有new,所以开辟了一块新空间,所有属性都有默认值,故输出结果为0;
扩展:
扩展:
public class Test{
public static void main(String[] args) {
new Student(30).print();
}
}
输出0,30
此时Student这个构造方法已经全部执行完成,最后才调用print方法,
print方法是通过子类Student类new调用,print方法被覆写,
所以调用的一定是被覆写后的方法。(先等于100,在等于30),
故输出30.
例子:
abstract class Person{
public abstract void print();
//在抽象类内部提供了一个实现好的子类,
//(静态方法可通过类名称直接调用)
public static Person getInstance(){
//方法内部类,继承了抽象类并实现,
//方法返回了实现好的子类
class Student extends Person{
public void print(){}
}
return new Student();
}
}
public class Test{
public static void main(String[] args) {
Person per = new Student();
per.print();
}
}
- 匿名内部类:等同于方法内部类,就是没有名字,但是匿名内部类必须继承一个抽象类或者实现一个接口,除此之外与方法内部类完全一致
例子:
abstract class Person{
public abstract void print();
//在抽象类内部提供了一个实现好的子类(静态方法可通过类名称直接调用)
public static Person getInstance(){
//匿名内部类,隐藏了没有名字的类
//等同于class 无名 extends Person{}
//直接实例化:new Person();
return new Person(){
//内部类
public void print(){}
}
}
}
.