一、继承
继承在面向对象开发思想中是一个非常重要的概念,在Java中使用extends关键字来标识两个类的继承关系。
继承的好处:继承是的整个程序框架具有一定的弹性,在程序中复用一些定义过的类可以减少软件开发周期,提高软件的可维护性和可扩展性。
继承的基本思想:基于某个父类的扩展,制定出一个新的子类,子类可以继承父类中的原有属性和方法,一可以增加原来父类中不具备的属性和方法,或者重写父类中的某些方法。
在Java中对于继承,Java只支持单继承,不支持多继承,但是保留了多继承的机制,进行改良。单继承:一个类只能有一个父类,多继承:一个类可以继承多个类。那么为什么不支持多继承呢 ?如果一个类同时继承了两个类,两个类中恰好有相同的功能,当子类调用此功能的时候,不知道调用那个父类里的方法,产生错误,但是支持多重继承,示例如下。
package com.itheima;
/**
* 创建三个类 Subroutine,Parent,SubParent。这三个类的继承关系是 Subroutine继承SubParent,SubParent
* 继承Parent
* @author Administrator
*
*/
class Parent{
Parent(){
System.out.println("我是Parent");
}
}
class SubParent extends Parent{
SubParent() {
// TODO Auto-generated constructor stub
System.out.println("我是SubParent");
}
}
class Subroutine extends SubParent{
Subroutine(){
System.out.println("我是Subroutine");
}
}
public class JiChengDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Subroutine sub=new Subroutine();//实例化子类对象
}
}
运行结果如下,
从上述结果可以看出当实例化子类Subroutine时,只调用了该子类的构造函数,并没有调用父类的构造函数,但是它却相应的调用了父类的构造方法。在结果中可以看到调用构造函数的顺序,首先是顶级父类,然后是上一级父类,最后是子类。
得出结论:实例化子类对象时首先要实例化父类对象,然后再实例化子类对象,所以子类调用父类的成员时,父类已经完成了实例化操作。
注意:在实例化子类对象时,父类无参构造函数将被自动调用,原因是子类的所有构造函数中的第一行,其实都有一条隐身的语句super();但是有参的构造函数不能自动被调用,只能依赖super关键字调用父类的成员。
子父类出现后,类中的成员有了哪些特点呢?
1、成员变量
当子父类中出现同样的属性时,子类类型的对象调用该属性,显示子类的属性值。
如果要使用父类中的属性值,需要使用关键字super。
(super与this的区别)super是子类所属父类的空间引用,而this代表本类类型的对象引用。
但是,一般情况下,子类是不会定义与父类类相同的变量名,直接继承过来就可以使用了。
2、成员函数
当子父类中出现同样的方法时,建立对象,父类中的同名函数会被覆盖掉,所以这是函数的一种特性又叫函数重写。重写与重载的区别就是重写是继承父类后堆父类中原有的函数重新编写代码,而函数重载是同一个类中使用同名函数但是参数个数不一样。
那么什么时候才会用到函数重写呢?
当一个类的功能需要修改时,可以通过函数重写来实现。
super()或者this():为什么一定要定义在第一行?因为super()或者this()都是调用构造函数,构造函数用于初始化,所以初始化的动作要先完成。
二、final关键字
final关键字可以修饰类,变量,函数,其中final修饰的类不能被继承,final修饰的方法不能被重写,final修饰的变量只能被赋值一次。
那么为什么要用final来修饰变量呢 ?在程序中如果一个数据是固定的。那么直接使用这个数据就可以了,但是这种阅读性差,所以应该给数据起个名称。而且这个变量名称的值不能变化,所以加上final固定。
写法规范:常量所有字母都大写,多个单词,中间用_连接。
final关键字在定义变量的时候必须对其变量进行赋值。具体看下面的例子。
package com.itheima;
/**
* 定一个类型的final ,验证
* @author Administrator
*
*/
class FinalTest {
int i=0;
}
public class FinalData
{
//定义一个final常量
private final int VALUE_1=9;
//定义一个static final常量
private static final int VALUE_2=10;
//定义一个final引用
private final FinalTest finalTest=new FinalTest();
//定义一个非final引用
private FinalTest finalTest2 =new FinalTest();
//定义一个final数组
private final int[] a={1,2,3,4,5,6};
public static void main(String[] args) {
// TODO Auto-generated method stub
FinalData data = new FinalData();
// data.finalTest=new FinalTest();
//可以对指定为final的引用中的成员变量赋值
//但不能将定义为final的引用指向其他引用
// data.VALUE_2++;
//不能改变定义为final的常量值
data.finalTest2 = new FinalTest();
// 可以将没有定义为final的引用指向其他引用
for (int i = 0; i < data.a.length; i++) {
// a[i]=9;
// //不能对定义为final的数组赋值
}
System.out.println(data);
System.out.println("data2");
System.out.println(new FinalData());
// System.out.println(data);
}
}
从上述例子中可以看出,经过final定义的常量,数组,对象引用等,在主函数中后不可以被重新改变。
那么为了让一个常量做到真正的不可改变,可以将常量声明为static final。
三、抽象类
抽象类的定义:抽象就是从多个事物中把共性的,本质的内容抽取出来。比如,美国人和中国人,都是人类。
如果一个类中声明一个抽象的方法,那么必须将存放这个方法的类定义成抽象类,不可能在非抽象类中定义非抽象方法。也就是抽象方法一定存在与抽象类中。抽象类语法如下:
public abstract class Test
{
abstract void method(){};//定义抽象方法
}
其中abstract是定义抽象的关键字。
抽象类不能实例化的原因:
1、抽象类是具体事物抽取出来的,本身就是不具体的,没有相对应的实例。
2、就算创建了实例,调用抽象方法也没有意义。
3、抽象类通过其子类进行实例化,子类需要覆盖掉抽象类中的所有抽象方法才可以创建对象,否则子类也是抽象类。
抽象类示例代码如下:
package com.itheima;
//定义一个职工类,包括职员基本信息,姓名,基本工资,家庭住址
abstract class Employee{
String name,address;
double basic;
//定义构造函数
Employee(String name,double basic, String address)
{ this.name=name;
this.basic=basic;
this.address=address;
}
//定义抽象方法,用于显示信息。
abstract void show();
}
//定义一个经理类继承于抽象类 职员类,需将抽象方法重写
class Manager extends Employee
{
//定义经理所在部门
String department;
//构造函数
Manager(String name,double basic, String address,String department)
{
super(name,basic,address);
this.department=department;
}
//对父类中的抽象方法进行重写
void show()
{
System.out.println("姓名是"+name);
System.out.println("基本工资是"+basic);
System.out.println("地址是"+address);
System.out.println("所属部门是"+department);
}
}
public class AbstractTest {
public static void main(String[] args)
{
Manager m=new Manager("张三",2000,"北京","管理");
m.show();
}
}
运行结果如下:
抽象类相关问题:
1、抽象类是否有构造函数? 有,用于子类的初始化
2、抽象关键字不能与哪些关键字一起使用?private final static
3、抽象类可不可以没有抽象方法?可以
抽象类与一般类的区别:
1、相同点就是都在内部定义了成员用来描述事物。
2、不同点是(1)一般类有足够的信息描述事物。抽象类描述事物的信息有可能不足。
(2)一般类中不能定义抽象方法,只能定义非抽象方法。抽象类中可定义抽象方法,同时也可以定义非抽象方法。
(3)一般类可以被实例化。抽象类不可以被实例化。
抽象类一定是父类,因为需要子类覆盖其方法后才可以对子类实例化。
四、接口
接口是抽象类的延伸,可以将它看成一个纯粹的抽象类。
接口使用interface关键字进行定义,其语法格式为:
public interface Test{
void method();//这里省略关键字abstract
}
成员常量:public static final
成员函数:public abstract
需要注意的是,接口中的方法必须被定义为public或abstract形式,没有声明默认就是public 的 ,在接口中定义的任何字段都自动是static和final的 。类与类之间是继承关系,类与接口直接是实现关系。接口不可以实例化,能由实现了接口并覆盖了接口中所有的抽象方法的子类实例化。否则,这个子类就是一个抽象类。
示例:
package com.itheima;
/**
*定义一个接口,存在一个show方法,功能是打印子类功能,有两个子类Parents父母类,Teacher老师类
* @author Administrator
*
*/
//定义接口
interface Check
{
public abstract void show();
}
//定义教师类,打印我是老师
class Teacher implements Check
{
//对父类中的抽象方法进行重写
public void show()
{
System.out.println("我是老师");
}
}
//定义父母类,打印我们是父母
class Parents implements Check
{
//对父类中的抽象方法进行重写
public void show()
{
System.out.println("我们是父母");
}
}
public class InterfaceTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Check c=new Teacher();
c.show();
c=new Parents();
c.show();
}
}
运行结果如下:
抽象类和接口的异同点?
1、相同点:
都是不断向上抽取而来的。
2、不同点:
(1).抽象类需要被继承,而且只能单继承。接口需要被实现,而且可以多实现。
(2).抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法。
接口中只能定义抽象方法,必须由子类去实现。
(3).抽象类的继承,是is a关系,定义该体系的基本共性内容。接口的实现是like a关系。