类的继承
1.继承的概念
在现实生活中,继承一般指子女继承父母的财产;而在程序中,继承是描述事物之间的关系的,通过继承可以使多种事物之间形参一种关系体系。例如,猫和狗都属于动物,那么程序中就可以描述为猫和狗继承自动物,同理,波斯猫和巴厘猫继承自猫科,沙皮狗和斑点狗继承自犬科,这些动物之间形成一个继承体系。
在java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的类叫子类,现有类为父类,子类继承父类的属性和方法,使得子类对象(实例)具有父类的属性和行为
在程序中,如果想要定义一个子类,需要使用extends关键字,格式如下:
class 父类{……}
class 子类 extendx 父类{……}
下面通过一个案例来学习子类是如何继承父类的
package zhiyin;
class Animal{
private String name;
private int age;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
}
class Dog extends Animal{
//此处不写任何代码
}
public class adw {
public static void main(String[] args) {
Dog dog = new Dog(); //创建一个Dog类的实例对象
dog.setName("牧羊犬"); //此时访问的方法是父类中的方法,子类中并没有定义方法
dog.setAge(3); //此时访问的方法是父类中的方法,子类中并没有定义方法
System.out.println("name:"+dog.getName()+"age:"+dog.getAge());//name:牧羊犬age:3
}
}
从上述代码中,我们可以得出,子类Dog中虽然没有任何定义操作,但是Dog类的对象仍能调用其父类的方法,这就说明了子类在继承父类的时候,会继承父类的成员。(好比说沙皮狗是犬科,但也是食肉动物,犬科动物肯定是具备食肉动物的某些特征和行为的)
接下来再通过一个案例,来学习在子类中定义自己的属性和方法:
package zhiyin;
class Animal{
private String name;
private int age;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
}
class Dog extends Animal{
private String color;
public String getColor(){
return color;
}
public void setColor(String color){
this.color = color;
}
}
public class adw {
public static void main(String[] args) {
Dog dog = new Dog(); //创建一个Dog类的实例对象
dog.setName("拉布拉多犬"); //此时访问的方法是父类中的方法,子类中并没有定义方法
dog.setAge(3); //此时访问的方法是父类中的方法,子类中并没有定义方法
dog.setColor("白色");
System.out.println("name:"+dog.getName()+"age:"+dog.getAge()+"颜色:"+dog.getColor());//name:拉布拉多犬age:3颜色:白色
}
}
在上述代码中,Dog类中增加了color属性、getColor、setColor方法。此时Dog类已有3个属性,和6个方法。其实想想就知道当我们在描述“狗”这个整体的共同特征的时候,因为不知道它是什么品种,所以无非就是:4条腿、1个头等等,但是让我们描述拉布拉多犬的时候,我们就知道它们是白色的、性格很温顺、很可爱,但同时它们也具有狗的共同特征
2.在类的继承中需要注意的一些问题
①在java中,类只支持单继承,不支持多重继承。也就是说,一个子类只能有一个直接父类(比如说狗的一个品种只能属于狗,但不属于猫),例如下面这种情况是不合法的:
class A()
class B()
class C extends A,B{} //C类不能同时继承两个类
②多个类可以继承同一个父类(比如说沙皮狗、牧羊犬等都属于狗),例如下面这种情况是合法的:
class A()
class B extends A {}
class C extends A {} //B和C类都可以继承A类
③在java中,多层继承是被允许的,即一个子类可以再继承另外的父类。例如,C类继承B类,而B类又继承A类,这时C类也可称为A类的子类(比如沙皮狗属于犬,而犬科也属于动物,那么沙皮狗也属于动物):
class A()
class B extends A {}
class C extends B {}
④在java中,子类和父类是一种相对概念,一个类可以是某个类的父类,也可以是某个类的子类。例如:在③中,B是C的父类,又是A的子类
⑤在继承中,子类不能直接调用和访问父类中的私有成员,但可以调用父类中的非私有方法
3.方法的重写
在类的继承中,子类会自动地调用父类中定义的非私有方法,但有时在子类中需要对继承的方法进行修改,即对父类的方法进行重写。在子类中重写的方法需要和父类被重写的方法具有相同的方法名、参数列表和返回值类型,且在子类重写的方法不能拥有比父类方法更加严格的访问权限。
1.package zhiyin;
2.class Animal{
3. void shout(){ //定义动物叫的方法
4. System.out.println("动物发出叫声");
5. }
6.}
7.class Dog extends Animal{
8. void shout(){ //重写父类Animal中的shout()方法
9. System.out.println("汪汪汪……");
10. }
11.}
12.public class adw {
13. public static void main(String[] args) {
14. Dog dog = new Dog(); //创建一个Dog类的实例对象
15. Animal animal = new Animal();
16. animal.shout(); //动物发出叫声//调用Animal类的shout方法
17. dog.shout(); //汪汪汪…… //调用Dog类重写的shout方法
18. }
19.}
上述代码中,子类重写了父类中的方法后,子类对象将无法访问父类中被重写的方法,为了解决这个问题,java提供了super关键字,super关键字可以在子类中调用父类的普通属性、成员方法和构造方法
super关键字
下面讲解super关键字的用法:
①使用super关键字访问父类的成员变量和成员方法,格式如下:
super.成员变量
super.成员方法(参数1,参数2,……)
案例如下:
package zhiyin;
class Animal{
String name ;
void shout(){ //定义动物叫的方法
System.out.println("动物发出叫声");
}
}
class Dog extends Animal{
public void shout(){ //重写父类Animal中的shout()方法
super.shout();
System.out.println("汪汪汪……");
}
public void printName(){
System.out.println("名字:"+super.name);
}
}
public class adw {
public static void main(String[] args) {
Dog dog = new Dog(); //创建一个Dog类的实例对象
dog.name = "牧羊犬";
dog.shout(); //动物发出叫声/n汪汪汪…… //调用Dog类重写的shout方法
dog.printName(); //名字:牧羊犬
}
}
②使用super关键字访问父类中指定的构造方法,格式如下:
super(参数1,参数2…)
案例如下:
package zhiyin;
class Animal{
private String name;
private int age;
public Animal(String name,int age){
this.name = name;
this.age = age;
}
public Animal() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Animal{name = " + name + ", age = " + age;
}
}
class Dog extends Animal{
private String color;
public Dog(String name,int age,String color){
super(name,age);
this.setColor(color);
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String toString() { //重写了父类中的toString方法
return super.toString()+",颜色 = "+this.getColor()+"}"; //相当于是扩充了父类的方法
}
}
public class adw {
public static void main(String[] args) {
Dog dog = new Dog("牧羊犬",3,"黑色"); //创建一个Dog类的实例对象
System.out.println(dog.toString());//Animal{name = 牧羊犬, age = 3,颜色 = 黑色}
}
}
利用super关键字访问父类方法的理解:
当一个类作为父类被继承时,子类会继承父类的所有属性和方法。子类在创建时需要调用构造函数来为子类的成员变量初始化,而构造函数的初始化过程必须先初始化父类的成员变量,才能继续初始化子类的成员变量。
package zhiyin;
class TestSuper {
TestSuper (int i) {
System.out.println("TestSuper");
}
}
class TestSub extends TestSuper {
TestSub(){
super(1); //调用了父类中的TestSuper(int i)方法
System.out.println("TestSub");
}
}
public class adw {
public static void main(String[] args) {
new TestSub ();
}
}
Java中的构造函数会默认调用父类的无参数构造函数,因此如果父类定义了无参数构造函数,子类的构造函数会默认调用父类的无参数构造函数完成初始化,子类的构造函数中也可以直接使用 super()关键字来调用父类的无参构造函数。
package zhiyin;
class TestSuper {
TestSuper () {
System.out.println("TestSuper");
}
}
class TestSub extends TestSuper {
TestSub(){
super(); //这里可写可不写
System.out.println("TestSub");
}
}
public class adw {
public static void main(String[] args) {
new TestSub ();
}
}
但是,如果父类没有无参数构造函数,则子类的构造函数必须在构造函数体的第一行调用父类的带参构造函数来完成父类的初始化,并传入必要的参数。这时在子类构造函数的第一行使用 super()关键字来调用父类的具有参数的构造函数,便可以完成父类构造函数的初始化操作,再基于此进行子类的成员变量初始化。
package zhiyin;
class TestSuper {
TestSuper (int i) {
System.out.println("TestSuper");
}
}
class TestSub extends TestSuper {
TestSub(){
super(1); //调用了父类中的TestSuper(int i)方法
System.out.println("TestSub");
}
}
public class adw {
public static void main(String[] args) {
new TestSub ();
}
}
注意:
通过super关键字调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次。 super和this关键字的作用非常相似,都可以调用构造方法、成员方法和属性,但是两者还是有区别的:
另外,this和super两者不能同时出现,因为this和super在调用构造方法时都要求必须放在构造方法的首行。
final关键字
final关键字可以声明类、属性、方法。需要注意以下几点:
①final修饰的类不能有子类
②final修饰的方法不能被子类重写
③final修饰的变量(成员变量或局部变量)是常量,常量不可修改
1.final关键字修饰类
final修饰的类将不可被继承,即不能派生子类。
2.final关键字修饰方法
final关键字修饰一个类的方法后,这个类的子类不能重写该方法
3.final关键字修饰变量
注意:final修饰变量时,最好全部字母大写。比如说一个变量使用public static final声明,则此变量将成为全局变量:public static final NAME = “we”;
抽象类和接口
1.抽象类
当定义一个类时,常常需要定义一些成员方法来描述类的行为特征,但有时这些方法的实现方式是无法确定的。例如,前面在定义Animal类时,shout()方法用于描述动物的叫声,但是不同的动物的叫声是不同的,因此shout()方法是无法准确地描述动物的叫声的。
针对上述问题,java提供了抽象方法来满足这个需求。抽象方法是使用abstract关键字修饰的成员方法,在定义抽象方法时不需要定义方法体,抽象方法的格式如下:
abstract 返回值类型 方法名称(参数);
当一个类包含了抽象方法,那么该类就必须是抽象类。抽象类和抽象方法一样,必须使用abstract关键字进行修饰,抽象类的格式如下:
abstract class 抽象类名称{
属性;
访问权限 返回值类型 方法名称(参数){
return 返回值;
}
访问权限 abstract 返回值类型 抽象方法名称(参数);
}
}
从上述格式中可以发现,抽象类的定义比普通类多了一些抽象方法,其他地方与普通类的组成基本上相同。
抽象类的定义规则:
①包含抽象方法的类必须是抽象方法
②抽象类和抽象方法都用abstract关键字修饰
③抽象方法只需声明而不需要实现
④如果一个类继承了抽象类,那么该子类必须实现抽象类中的全部抽象方法
1.package zhiyin;
2.abstract class Animal{ //定义抽象类
3. abstract void shout(); //定义抽象方法shout()
4.}
5.class Dog extends Animal{
6. @Override
7. void shout() {
8. System.out.println("汪汪");
9. }
10.}
11.
12.public class adw {
13. public static void main(String[] args) {
14. Dog dog = new Dog();
15. dog.shout(); //汪汪
16. }
17.}
注意:使用abstract关键字修饰的抽象方法不能用private关键字修饰,因为抽象方法必须被子类实现,如果该抽象方法使用了private,那么子类就无法实现该抽象方法
2.接口
当一个抽象类的所有方法抽象的,则可以将这个类定义接口。接口是java中最重要的概念之一。在JDK8中,接口除了可以包括抽象方法,还可以包括默认方法和静态方法(静态方法也叫类方法),其中默认方法使用default修饰,静态方法使用static修饰,且这两种方法都允许有方法体。
接口使用interface修饰,格式如下:
public interface 接口名 extends 接口1,接口2...{
public static final 数据类型 常量名 = 常量值;
public abstract 返回值类型 抽象方法名称(参数列表);
}
//java使用接口的目的是克服单继承的限制,因为一个类只能有一个父类,而一个接口可以同时继承多个父接口。
接口的规则:
1."extends 接口1,接口2..."表示一个接口可以有多个父接口,父接口之间用逗号分割。
2.接口中的变量默认是public static final的,即全局变量。
3.接口中定义的方法默认使用public static abstract进行修饰,即抽象方法。
4.如果接口声明为public,那么该接口中的常量和方法全部为public。
未完