一、继承的概念
- 继承是类与类之间的关系,是一个很简单很直观的概念,与现实世界中的继承(例如儿子继承父亲财产)类似。
- 继承可以理解为一个类从另一个类获取方法和属性的过程。如果类B继承于类A,那么B就拥有A的方法和属性。
- 继承使用 extends 关键字。
例如:Son.java继承Person.java
public class Son extends Person {
}
二、父类与子类
父类:Person.java
public class Person {
/**
* 姓名
*/
private String name;
/**
* 性别 1:男;0:女
*/
private int gender;
/**
* 年龄
*/
private int age = 18;
/**无参构造方法
*/
public Person() {
}
/**
* 有参构造方法
*/
public Person(String name, int gender, int age) {
super();
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
子类:Son.java
public class Son extends Person {
}
三、测试
public class SonTest {
@Test
public void tets01() {
Son son01 = new Son();//此处继承的是无参构造方法;
son01.setName("光小强");
son01.setGender(1);
son01.setAge(15);
System.out.println("姓名:" + son01.getName() + "\n性别:" + (son01.getGender()==1?"男":"女") + "\n年龄:" + son01.getAge());
}
}
运行结果:
姓名:光小强
性别:男
年龄:15
以上表明:
- 子类可以继承父类的属性和方法
- 子类无法直接继承父类的构造方法
- 子类在测试类中可以直接Son son01 = new Son();(看似继承了父类的无参构造),是因为子类在形成时自身创建了一个无参构造方法
四、子类继承父类构造方法并更改参数
public class Son extends Person {
public Son() {
}
public Son(String name, int gender, int age) {
this.setName(name);
this.setGender(gender);
this.setAge(age);
}
}
或:
public class Son extends Person {
public Son() {
}
public Son(String name, int gender, int age) {
super.setName(name);
super.setGender(gender);
super.setAge(age);
}
}
测试:
@Test
public void test03() {
Son son01 = new Son("熊大", 1, 2);
System.out.println("姓名:" + son01.getName() + "\n性别:" + (son01.getGender()==1?"男":"女") + "\n年龄:" + son01.getAge());
}
运行结果:
姓名:熊大
性别:男
年龄:2
五、实例化顺序
先实例化父类,再实例化子类
实例化子类时,先调用的是父类的构造方法
每实例化一个子类,都会先调用父类的构造方法
(注意:是构造方法,不是方法)
先在父类的无参构造方法中输入:
public Person() {
System.out.println("父类的无参构造方法");
}
再在两个子类的无参构造方法中分别输入:
public Son() {
System.out.println("第一个子类的无参构造方法");
}
和
public Son01(){
System.out.println("第二个子类的无参构造方法");
}
测试:
@Test
public void test04() {
Son son = new Son();
Son01 son01 = new Son01();
}
运行结果:
父类的无参构造方法
第一个子类的无参构造方法
父类的无参构造方法
第二个子类的无参构造方法
六、子类可以有自己独有的方法
先在父类中新建一个方法
public void eat() {
System.out.println("父类里的eat方法");
}
在第一个子类中新建自己独有的方法:
public void work() {
System.out.println("第一个子类中的work方法");
}
在第二个子类中同样新建自己独有的方法:
public void money() {
System.out.println("第二个子类中的money方法");
}
测试:
@Test
public void test05() {
Son son = new Son();
Son01 son01 = new Son01();
son.work();
son01.money();
son.eat();
son01.eat();
}
运行结果:
父类的无参构造方法
第一个子类的无参构造方法
父类的无参构造方法
第二个子类的无参构造方法
第一个子类中的work方法
第二个子类中的money方法
父类里的eat方法
父类里的eat方法
七、方法重写
想在子类中重写父类中的一个或多个方法:
在子类中 右键—>source—>override—>选择想要重写的方法—>OK
测试:
父类中的eat方法
public void eat() {
System.out.println("父类里的eat方法");
}
子类中的eat方法(重写父类的eat方法)
@Override
public void eat() {
System.out.println("子类中的eat方法");
}
测试:
@Test
public void test06() {
Son son = new Son();
son.eat();
}
运行结果:
父类的无参构造方法
第一个子类的无参构造方法
子类中的eat方法
八、父类的引用指向子类的实例化对象(向上转型)
将子类实例化对象类型换成父类,输出还是子类的方法
测试:
@Test
public void test07() {
Person son = new Son();
son.eat();
}
父类的无参构造方法
第一个子类的无参构造方法
子类中的eat方法
- 前提new的是子类:父类的引用指向子类的实例化对象
- 调用的还是子类中重写的方法
- 不能调用子类独有的方法
九、向下转型
父类直接转换成子类类型是没有办法调用子类方法的
错误转换:
@Test
public void test08() {
//只有引用类型对象的真实身份为子类对象才可以转换;
Son person = (Son) new Person("熊二",1,12);
person.eat();//父类直接转子类无法调用任何方法
System.out.println("姓名:" + person.getName() + "\n性别:" + (person.getGender()==1?"男":"女") + "\n年龄:" + person.getAge());
}
正确转换:
@Test
public void test09() {
//先向上转型
Person person = new Son();
//再向下转型,实际引用类型对象还是子类,所以不管person.eat还是son.eat调用的都是子类中的eat方法
Son son = (Son) person;
person.eat();
son.eat();
//person.work();无法调取,因为对象类型发生准便,不能调用子类独有的方法
son.work();
((Son)person).work();//这个其实还是将数据类型转换成子类的,就可以调用子类中的work方法,这是强制数据类型转换
}
运行结果:
父类的无参构造方法
第一个子类的无参构造方法
子类中的eat方法
子类中的eat方法
第一个子类中的work方法
第一个子类中的work方法
十、方法重载和方法重写
重载看真实的数据类型,重写看真正的引用对象
public class InheritanceTest {
public static void main(String[] args) {
Super s = new Sub();
Goo goo = new Goo();
goo.g(s);//s的数据类型是Super,所以找数据类型g中是Super的obj;但s真正的引用对象是Sub,所以obj.f找的是Sub类,因此整体输出结果是g(Super) Sub.f
}
}
class Super{
public void f() {
System.out.println("Super.f");
}
}
class Sub extends Super{
@Override
public void f() {
System.out.println("Sub.f");
}
}
class Goo{
public void g(Super obj) {//数据类型是个类,那它就是个对象,obj
是个对象
System.out.println("g(Super)");
obj.f();
}
public void g(Sub obj) {
System.out.println("g(Sub)");
obj.f();
}
}
运行结果:
g(Super)
Sub.f
方法重载和方法重写的区别:
- 方法重载:
在同一个类,参数列表不同的同名方法我们称之为方法重载; - 方法重写:
父类的方法满足不了子类需求,子类重写父类的方法我们称之为方法重写; - 方法重载在同一个类中而方法重写必须存在子父类继承关系。
十一、this调用构造方法
需求:在一个类中限定属性中一个数组的长度:
方法一:在测试类中调用有参构造并进行赋值;
private int[] capacity;
public Son02(int[] capacity) {
super();
this.capacity = capacity;
}
public int[] getCapacity() {
return capacity;
}
public void setCapacity(int[] capacity) {
this.capacity = capacity;
}
测试:
@Test
//传参调用的是有参构造;
public void test01() {
Son02 son02 = new Son02(new int[10]);//给capacity这个数组传参,参数表示数组的长度
System.out.println(son02.getCapacity().length);//输出这个数组的长度;
}
方法二:使用使用无参构造赋值
private final int defaultCapacity = 10;//给一个变量赋值,final将这个变量定为常量,令数组的长度等于这个变量的值;这样就可以默认这个数组的长度;
private int[] capacity;
public Son02() {
capacity = new int[defaultCapacity];//默认这个数组的长度是10;
}
public Son02(int[] capacity) {
super();
this.capacity = capacity;
}
public int[] getCapacity() {
return capacity;
}
public void setCapacity(int[] capacity) {
this.capacity = capacity;
}
测试:
@Test
//这个调用的是无参构造;
public void test02() {
Son02 son02 = new Son02();
System.out.println(son02.getCapacity().length);
}
方法三:使用this调用构造方法
private final static int defaultCapacity = 10;//想用this调用构造方法,这里必须是静态;
private int[] capacity;
public Son03() {
//capacity = new int[defaultCapacity];//默认这个数组的长度是10;
//可以改变为
this(new int[defaultCapacity]);//this是用来调用本类中的构造方法的;
}
public Son03(int[] capacity) {
super();
this.capacity = capacity;
}
public int[] getCapacity() {
return capacity;
}
public void setCapacity(int[] capacity) {
this.capacity = capacity;
}
测试:
@Test
public void test02() {
Son03 son03 = new Son03();
System.out.println(son03.getCapacity().length);
}
注意:
private final static int defaultCapacity = 10;
private int[] capacity;
public Son04() {
//this(new int[defaultCapacity]);//this是用来调用本类中的构造方法的;
this(10,20);//这个this和上一行的this不能同时存在
//this在构造方法中调用其他构造方法只能放在第一行(一个构造方法中只能存在一个this去调用其他构造方法);注意使用this调用构造方法时不允许相互使用,造成死循环;
}
public Son04(int[] capacity) {
super();
this.capacity = capacity;
}
public Son04(int a, int b) {
super();
}
public int[] getCapacity() {
return capacity;
}
public void setCapacity(int[] capacity) {
this.capacity = capacity;
}
总结:
- this是在构造方法中使用的;
- this可以调用本类中的其他构造方法;
- this调用其他构造方法只能放在第一行(即一个构造方法中只允许有一个this去调用其他构造方法);
- 使用this调用构造方法时不允许相互使用,造成死循环;
十二、super调用父类构造方法
super可以调用父类里的构造方法;
public class Son extends Person {
public Son() {
super("熊二", 1, 13);//此处调用的是父类里的全参构造方法;要想传值,每个类都要有全参构造
}
}
测试:
测试类中不需要再进行赋值;
@Test
public void test03() {
Son son = new Son();
System.out.println("姓名:" + son.getName() + "\n性别:" + (son.getGender()==1?"男":"女") + "\n年龄:" + son.getAge());
}