Java入门(二十三)
继承
- java中的继承和现实生活中的继承是一样的,比如动物是个很高的分类(哺乳动物,爬行动物),比如哺乳动物再往下细分,一层一层地分级。之前学的封装是对一个对象、类的封装,比如类有属性、方法。属性加方法构成了类,如果类太多了是也得抽象。比如国家,有美国人、中国人、日本人。人是很广泛的,下面再通过国家,色种来判断细分的类。
- 继承的本质是对一批类的抽象,对类进行抽象。从而让代码实现建模,extends的意思是扩展,比如儿子继承了爸爸所有的东西,都是一样的。
- 我们写两个类,Person类(人类)。人类下面按学生跟老师的类来分。学生他也是人
- 这样extends,学生就继承了所有人的特点
public class Student extends Person {
}
- 再用Teacher类去继承,这样就有棵树的概念了,Person在上面,Teacher和Student再下面。
public class Teacher extends Person{
}
- 这个Teacher类可以细分成很多老师,但是他上面有一个Person类
- 这种独立的类可以再进行抽象,于是把他变成了一个父类。这下面所有的类都跟父类有一点关系。继承是最核心的东西,封装是底层,多态也是有继承的前提。
- 继承就是扩展,子类是父类的扩展。
- Student和Teacher可以叫做派生类、子类。除了继承,java还有其他关系,比如Student想用人,除了继承还可以组合,我可以把人组合成一个属性,Person person就相当于把Person也拿到了。
public class Student{
Person person;
}
- 继承是类跟类之间的关系,除此之外,还有依赖、组合、聚合关系。
- 子类和父类具有 is a 的关系
- 子类可以继承父类的所有方法,前提是public的方法。下面在父类里面写了一句话,而子类Student没有
public class Person {
public void say(){
System.out.println("说了一句话");
}
}
- 我们来看看学生能调用哪些方法,正常来说他只有一个无参构造方法,但是这里看到有say方法。
- 子类继承了父类,就会拥有父类的全部方法,不仅是全部的方法,比如说继承了父亲所有的钱
public class Person {
public void say(){
System.out.println("说了一句话");
}
public int money = 10_0000_0000;
}
- 父亲有了钱,但是儿子现在没有钱。我们来输出一下这个钱
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();
System.out.println(student.money);
}
}
-
只要子类继承了父类,他就会拥有父类全部的方法,前提是修饰符是public。private私有的东西不能用了
-
如果你要让子类继承一些东西,那这些东西不可能是私有的,私有的东西无法继承。还有的权限是default(不写就是这个)、protect。
-
按照封装思想,父类的东西也应该写成私有的。private int money,然后给他设置公共的方法。就是你无法直接操作我这个钱,但是我给你留了一些方法,你可以按照方法来进行操作。
-
ctrl + h 打开一棵树,继承树从关系而来。
-
Object类是什么?new 一个Person。在Person类为空的情况下,可以点出那么多方法,equals(),hashCode()因为有一个看不见的类,但实际存在。在java中所有的类都默认直接或间接继承Object类。
-
Object类再java.lang包下
-
来看下Object类是什么东西,有个地方叫getClass
Person person = new Person();
person.getClass();
- Java中只有单继承,没有多继承。一个儿子只能有一个爸爸,一个爸爸可以有多个儿子。
super
- super关键词可以和this进行对比,一个是代表父,一个是代表当前的。
- 我现在要在Person类里面定义一个属性,定义成protected受保护的属性。
Person类
public class Person {
protected String name = "kuangshen";
}
子类Student
public class Student extends Person {
private String name = "qinjiang";
public void test(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
- 结果发现用了super之后,子类可以访问到父类的属性
- 主程序测试一下
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.test("qinjiang");
}
}
- name输出了秦疆,第二个 this.name输出了当前类的qinjiang,最后一个super输出了父类。this跟super有区别。
- 除此之外,还可以调用父类的一些方法。
public class Person {
protected String name = "kuangshen";
public void print(){
System.out.println("Person");
}
}
- 子类也来写一个方法
public void print(){
System.out.println("Student");
}
- 来访问一下
public class Student extends Person {
public void print(){
System.out.println("Student");
}
public void test1(){
print();
this.print();
super.print();
}
public void test(String name){
}
}
主程序
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.test("秦疆");
student.test1();
}
}
- 前面两个都是Student,当前类的对象。最后一个输出了Person,调用了父类的方法。那现在假设我把父类的print()方法变成private私有的。通过super也调不了了,因为权限有问题了。私有的东西无法被继承。
- 类默认都有一个构造器,我们在Person类里面弄个无参的。正常调用一个对象首先会用无参构造器。
public Person(){
System.out.println("Person无参执行了");
}
- Student也来一个
public Student(){
System.out.println("Student子类无参构造执行了");
}
- 现在主程序上 new 一个Student会执行的操作会有哪几个呢?
public class Application {
public static void main(String[] args) {
Student student = new Student();
}
}
- 结果是先执行了Person再执行Student
- 说明这里面又有一个隐藏代码,他在Student.java里面默认调用了父类的无参构造。相当于在Person类里面有一行代码 super(); 。 假设你把这句代码显式地调用了出来,就会报错了,使用super(),必须放在子类构造器的第一行。
- 假设父类没有无参构造器怎么办?只要写了有参构造之后,无参构造就没了。
public Person(String name){
System.out.println("Person无参执行了");
}
- 一个类无论怎么写,只要你重写了无参构造,一般是先把无参加上。
- super注意点
- super调用父类的构造方法,而且必须在构造方法的第一个
- super必须只能出现在子类的方法,或构造方法中
- super和this不能同时调用构造方法
- this 对比 super
1 本身调用者的对象
2 super代表父类对象的应用
3 this:在没继承情况下也可以使用
4 super:只能在继承条件下才能使用
5 构造方法区别: this调用的是本类的构造,super调用的是父类的构造
方法的重写
- 现在我们new一个A类,一个B类
- 首先A类继承B类。两个就有父子关系了
public class A extends B{
}
- 然后A类跟B类都写一个方法。重写都是方法的重写,和属性无关。
public class A extends B{
public static void test(){
System.out.println("A=>test()");
}
}
public class B {
public static void test(){
System.out.println("B=>test()");
}
}
- new A类的对象
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();
}
}
- a.test()成功输出,再来操作
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();
B b = new A();
b.test();
}
}
- b.test()也输出了,发现new的都是a,但是结果却不同了。a.test()调用到的是A类静态的方法。b.test()走的是B类的方法。A类和B类本身有继承关系,new A的时候,子类可以指向父类。B类是父类,父类的引用指向的是A。
- 方法的调用只和左边的类型有关,也就是定义的数据类型有关。现在把两边的static去掉
public void test(){
- 去掉之后,ide左侧多了两个圈。o向下,o代表重载,
- alt Insert (ctrl enter)可以看到有Override Methods。
- 将A类的删了,直接重写。@Override是注解,有功能的注释。
public class A extends B{
@Override
public void test() {
super.test();
}
}
- 再加上一行代码跑一遍
public class A extends B{
@Override
public void test() {
System.out.println("A=>test()");
}
}
- 刚才是调用A的时候走a的方法,调用B的时候走的是b的方法。突然发现现在两个都走了a的方法。可以得出一个结论:静态方法跟非静态方法有区别
- 加了static静态方法,他的调用跟左边有关。去掉static之后是非静态方法,new了A指向a,new了B指向b,这就叫重写了b的方法,子类重写了父类的方法。这个方法只跟非静态方法有关,重写跟静态方法没有任何关系。idea出现了圆圈箭头才是重写。
- 重写的关键词必须是public,不能是private。
- 总结:
- 重写需要有继承关系,子类重写父类的方法。
- 方法名必须相同
- 参数列表必须相同,否则变为重载
- 修饰符:范围可以扩大,但是不能缩小。public>protected>default>private 。
- 重写可能抛出异常,异常范围可以被缩小,但是不能扩大。
- 重写就是子类方法和父类必须一致,方法体不同。
- 为什么需要重写?首先父类的功能子类不一定需要,不一定满足。