1.减少代码的冗余,提高代码复用性
2.便于功能的扩展
3.为多态的使用提供前提
Class A extends B{}
A:子类、派生类、subclass
B:父类、基类、superclass
一旦A继承B之后,A就获取了B中的所有的属性和方法
特别的,父类中声明为private的属性和方法,子类继承父类后,仍然获取了父类中私有的结构,只是因为封装性的影响,使得子类不能直接调用父类的结构而已
子类继承父类以后,子类还可以声明自己特有的属性和方法:实现功能的扩展(extends:扩展)
1.一个类可以被多个子类继承
2.一个类只能有一个父类:Java类的单继承性(和c++的多继承不一样)
3.子父类是相对的概念,如下图2,父类2是子类的直接父类,父类1是子类的间接父类
4.子类可以获取直接父类和所有间接父类的属性和方法
子类继承父类后,可以对父类中同名同参数的方法,进行覆盖操作(重写的参数列表也是相同的,重载的参数列表是不同的)
重写以后,当创建子类对象后,通过子类对象调用子类中与父类同名同参数的方法时,实际执行的是子类重写父类的方法
权限修饰符 返回值类型 方法名(参数列表)throws 异常类型 {方法体}
1)子类重写的方法的方法名和形参列表和父类被重写的方法的相同
2)子类重写的方法的权限修饰符 >= 父类被重写的方法的权限修饰符
特殊情况:子类不能重写父类中声明为private权限的方法
3)返回值类型:
i.父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型也是void
ii.父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
如父类中的一个方法:public Object eat(){},则子类中重写可以写成public String eat(){},因为String是Object的子类
iii.父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的数据类型
4)子类重写的方法抛出的异常类型 <= 父类被重写的方法的抛出的异常类型(具体看异常处理的内容)
总结:权限大于父亲,返回和异常小于父亲
一般开发中都是直接把父类的方法粘贴到子类中再重写方法体的
子类和父类同名同参数的方法要么声明为非static的(这时要考虑重写的情况),要么都声明为static的(静态的就不是重写了)。
对于重写,指的是方法,方法重写之后,子类的方法覆盖父类的方法。而对于属性,子类的属性(如id)和父类的属性(如id)相同时,不能形成覆盖,即子类中既有子类的id又有父类的id。
不同包的子类只能调用父类的protected和public权限的结构(同一个包也是一样的)
不同包的普通类只能调用另一个包下面的类的public结构
1.可以在子类的方法或构造器中,通过“super.属性”或“super.方法”的方式显式的调用父类中声明的属性或方法。但是通常情况下省略“super.”
2.特殊情况,当子类和父类定义同名的属性时,要想在子类调用父类中声明的属性,则必须显式的使用“super.属性”的方式,表明调用的是父类中的属性。
如
class Person{int id;}
class Student extends Person{int id;}
则直接在子类的方法或构造器中写id代表的是子类的id;this.id也代表子类的id;super.id代表是父类的id
3.当子类重写父类中的方法后,想在子类的方法中调用父类中被重写的方法时,则必须显式的使用“super.方法”的方式,表明调用的是父类中被重写的方法。
总结:在子类中想调子类的结构,啥都不加或加“this.”;想调父类的结构,加“super.”
1.可以在子类的构造器中显式的使用“super(形参列表)”的方式,调用父类中指定的构造器
如
public class Person {
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Student extends Person{
Student(String name, int age, String major){
super(name, age);//调用父类构造器
this.major = major;
}
}
2.“super(形参列表)”需声明在子类构造器的首行。所以在构造器中只能在“this(形参列表)”和“super(形参列表)”二选一,不能同时出现
3.在构造器首行没有显式的声明“this(形参列表)”和“super(形参列表)”,则默认调用的是父类中空参的构造器:super()
如
public class Student extends Person{
Student(String major){
this.major = major;//没有“this(形参列表)”和“super(形参列表)”,则默认在这个构造器中有一个super();
}
}
所以父类中一定要有空参构造器,否则创建子类的时候构造器会出错(出错提示:Implicit super constructor Person() is undefined. ),因为要调用spuer();
4.在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)“。
子类继承父类后,就获取父类中的结构,即创建子类对象时,在堆空间就会加载所有父类的属性和方法
通过子类的构造器创建子类对象时,一定会调用其直接父类的构造器,进而调用间接父类的构造器…直到java.lang.Object类中空参的构造器为止。正因为调用super(形参列表)时会调用父类构造器(调用父类构造器类似于创建父类对象,所以会加载父类的结构),所以才能在内存中看到父类中的结构。
虽然创建子类对象时调用了父类的构造器,但是自始至终就创建过一个对象,即new的子类对象。
class Person {方法:eat(); walk();}
class Man extends Person{重写方法:eat(); walk(); 方法:earn();}
class Woman extends Person{重写方法:eat(); walk(); 方法:makeUp();}
则Person p = new Person();
或Person p = new Man();//多态
或Person p = new Woman();//多态
虚拟方法调用(所谓虚拟方法:形成多态时,父类的方法被称为虚拟方法,父类根据赋给它不同的子类对象,动态的调用属于子类的该方法。这样的方法调用在编译阶段是无法确定的)
进行父类的引用指向子类的对象的多态以后,在编译阶段,只能调用父类中声明的方法(如p.eat();p.walk();),但在运行阶段,实际执行的是子类重写父类的方法(如子类中的eat()和walk())
总结:编译看左,运行看右(Person p = new Man()的左和右)
编译的时候是父类行为,运行时是子类行为
1.有类的继承
2.子类重写父类方法
3.父类的引用指向子类的对象
public class AnimalTest {
public static void main(String[] args) {
AnimalTest aTest = new AnimalTest();
aTest.func(new Dog());
aTest.func(new Cat());
}
public void func(Animal animal) {//父类引用指向子类对象,形成多态
animal.eat();
}
}
class Animal{
public void eat() {
System.out.println("吃东西");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("吃屎");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("吃鱼");
}
}
public void func(Animal animal) {
animal.eat();
}
改成
public void func(Dog dog) {
dog.eat();
}
public void func(Cat cat) {
cat.eat();
}
代码量增加了
class Person {属性:id=1}
class Man extends Person{属性:id=2}
Person p = new Man();
P.id;//输出的是Person的id,即id=1
重载:调用地址在编译阶段就绑定了,称为”静态绑定“
多态:运行的时候调用地址才绑定,称为”动态绑定“
方式一:硬看,必要的时候在代码中间添加输出语句,看看输出的中间结果和自己想的是否一样
方式二:debug调试
当代码过长时,在每个类、方法前加文档注释,之后调用时可以方便的知道每个类和方法是干什么用的
1.设置断点(注意:可以设置多个断点)
2.debug as java application
3.常用操作
操作 | 作用 |
---|---|
step into 跳入(f5) | 进入当前行所调用的方法中 |
step over 跳过(f6) | 执行完当前行的语句,进入下一行 |
step return 跳回(f7) | 执行完当前行所在方法,进入下一行 |
drop to frame | 回到当前行所在方法的第一行 |
resume 恢复 | 执行完当前行所在断点的所有代码,进入下一个断点,如果没有就结束 |
这是所要debug的代码,输出如下
所以代码有问题。现在开始debug
在蓝色区域双击即可设置断点(再双击一次断点就会取消)
设置断点是为了使程序执行到断点停了一下,然后看看断点处的变量值和自己想的是否符合