- 什么是继承?
继承主要解决的问题:共性的抽取,实现代码复用。继承最大的意义就是将共性进行抽取,实现对代码复用。
比如,定义了一个Dog类和一个Cat类,我们会发现两个类中存在大量的代码重复,因此我们可以抽象出两者的共性,创建一个动物Animal类,然后让Dog类和Cat类继承Animal类。
故代码如下:
class Animal {
public String name;
public int age;
public void eat() {
System.out.println(name + "在吃饭");
}
}
class Dog extends Animal {
public void wang() {
System.out.println(name + "汪汪汪");
}
}
class Cat extends Animal {
public void miao() {
System.out.println(name + "喵喵喵");
}
}
其中Animal叫做:父类/基类/超类,Dog和Cat叫做:子类/派生类。
继承之后,子类就可以复用父类中成员,子类在是实现时只需关心自己新增加的成员。【进一步体现出继承最大的作用:实现代码复用】
注意:
①子类会将父类的成员变量或者成员方法继承到子类中。
②子类继承父类后,必须要添加自己特有的成员(成员变量或者成员方法),体现出与基类的不同。
- 子类访问父类的成员变量
①子类和父类不存在同名的成员变量
class Base {
public int a;
public int b;
}
class Derived extends Base {
public int c;
public int d;
}
public class Test2 {
public static void main(String[] args) {
//子类中的成员变量名不存在和父类同名
Derived derived = new Derived();
System.out.println(derived.a);
System.out.println(derived.b);
System.out.println(derived.c);
System.out.println(derived.d);
}
}
运行结果:
分析:a和b是访问从父类中继承下来的a、b,c和d是子类自己有的,因此是访问子类自己的c、d。
②子类和父类成员变量同名
class Base {
public int a;
public int b;
public int c = 100;
}
class Derived extends Base {
public int c = 99;
public int d;
public void fun() {
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
public class Test2 {
public static void main(String[] args) {
//子类中的成员变量名和父类同名
Derived derived = new Derived();
derived.fun();
}
}
在代码中,子类有成员变量c并且初始化值为99,父类也有成员变量c并且初始化值为100。那么输出c的时候,运行结果为:最后输出c的值为子类中的99。
分析:
①如果访问的成员变量子类中有,优先访问自己的成员变量。
②如果访问的成员变量子类中没有,则访问父类继承下来的,如果父类中也没有定义,则编译报错。
③如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
④成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
- super的用法——访问父类成员变量
在上述中打印c,因为子类有成员变量c,会优先访问子类自己的成员变量c,那么想要访问父类的成员变量c,则会用到super。
public void fun() {
System.out.println(a);
System.out.println(b);
System.out.println(super.c);
System.out.println(d);
}
运行结果:
分析:暂且可以把super简单理解为代表父类的引用。但实际上super只是一个关键字,不能说是代表引用,因为根本就没有实例化父类对象,所以哪来的引用?super最大的作用其实就是在写代码的时候或者读代码的时候体现出更好的可读性。而this是代表当前对象得引用。
- 子类访问父类的成员方法
①子类的成员方法名和父类的成员方法名都不相同
class Base {
public int a;
public int b;
public int c = 100;
public void methodBase() {
System.out.println("我是父类的成员方法");
}
}
class Derived extends Base {
public int c = 99;
public int d;
public void methodDerived() {
System.out.println("我是子类的成员方法");
}
public void fun() {
methodBase();
methodDerived();
}
}
public class Test2 {
public static void main(String[] args) {
//子类中的成员方法名和父类不同名
Derived derived = new Derived();
derived.fun();
}
}
运行结果:
分析:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,优先访问自己的,自己没有时再到父类中找(子类继承了父类所有的方法和变量),如果父类也没有则报错。
②子类成员方法名和父类成员方法名相同时
class Base {
public int a;
public int b;
public int c = 100;
public void methodFun() {
System.out.println("我是父类的成员方法");
}
}
class Derived extends Base {
public int c = 99;
public int d;
public void methodFun() {
System.out.println("我是子类的成员方法");
}
public void fun() {
methodFun();
}
}
public class Test2 {
public static void main(String[] args) {
//子类中的成员方法名和父类不同名
Derived derived = new Derived();
derived.fun();
}
}
运行结果:
分析:这里的子类和父类中都有methodFun方法,但是最后调用执行的却是子类的methodFun方法。这是因为优先在子类中找。
- super的用法——访问父类成员方法
修改上面的代码,访问父类的methodFun方法,则通过super关键字来完成。
public void fun() {
super.methodFun();
}
运行结果:
- 继承中的方法重载
重载:并不是一定在同一个类中,如果是继承关系,此时满足重载的条件,也可也看作是重载。
If two methods of a class (whether both declared in the same class, or both inherited by a class, or one declared and one inherited) have the same name but signatures that are not override-equivalent, then the method name is said to be overloaded.
class Base {
public int a;
public int b;
public int c = 100;
public void overFun() {
System.out.println("我是父类的成员方法");
}
}
class Derived extends Base {
public int c = 99;
public int d;
public void overFun(int n) {
System.out.println(n);
System.out.println("我是子类的成员方法");
}
public void fun() {
overFun(100);
}
}
public class Test2 {
public static void main(String[] args) {
//子类中的成员方法名和父类不同名
Derived derived = new Derived();
derived.fun();
}
}
}
}
运行结果:
分析:
如果父类和子类同名方法的参数列表不同(重载),根据第哦啊用方法传递的参数选择合适的方法访问。
-
super关键字
背景:由于设计不好or场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,直接访问时做不到的,要用到super关键字。
作用:Java提供了super关键字,该关键字的主要作用是:在子类方法中访问父类的成员。
因此,在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可。
【注意事项】
①super和this只能在非静态方法中使用,因为静态方法与对象无关,static不依赖于对象,而super和this都是和对象息息相关的。
②super可以在子类方法中,访问父类的成员变量和成员方法。(不能访问爷爷类) -
子类构造方法
在继承关系上,当我们在构造子类时,一定要先帮助父类进行构造。
怎么帮父类进行构造?------->在子类里调用父类构造方法。
子类在构造的时候,一定要先帮助父类进行构造!!!
父子父子,先有父再有子,即:子类对象构造时,需要先调用父类构造方法,然后执行子类的构造方法。
当在父类中加入有参构造方法时,Dog和Cat子类会提示报错。
这是因为在父类中写了有参构造方法,而子类时默认调用super()【无参构造方法】,此时编译器在父类中不会提供默认的无参构造方法。
解决办法:
①在父类中加上无参构造方法
②在子类中写出构造方法,并且调用父类的构造方法
为什么一开始没有写任何构造方法的时候程序能够执行?
因为 当父类和子类 都没有提供任何的构造方法时
父类:编译器默认提供无参构造方法
public Animal() {
}
子类:编译器默认提供无参构造方法,并且第一行编译器默认提供super()调用父类无参构造方法
public Dog() {
super(); //不写的话编译器会默认加上
}
总结:子类对象中成员由2部分组成,父类继承下来的以及子类新增的。所以在构造子类对象的时候,先要调用父类的构造方法,将从父类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增的成员初始化完整。
注意:
①如果父类显示定义无参构造方法或者默认的构造方法,在子类构造方法的第一行都会默认有隐藏的super()调用,super()的作用是调用父类的构造方法
②如果父类构造方法是带参数的,则此时需要为子类显示定义构造方法,并且在子类构造方法中选择合适的父类构造方法调用。
③在子类构造方法中,super()调用父类构造时,必须是子类构造函数中第一条语句,又因为this调用构造方法也必须是第一条语句,因此super和this在调用构造方法时,不能同时出现。
④super(),仅仅是显示调用父类的构造方法,帮助父类的成员进行初始化,并没有构造一个父类对象,仅仅是帮你堆继承过来的属性(在子类的对象中,即堆内存中存储)进行初始化。
综上,在我们构造子类的时候,一定要先帮助父类进行构造【想办法通过super调用父类的哪个方法进行构造】
- super和this
super和this都可以在成员方法中访问成员变量和调用其他成员方法以及作为构造方法的第一条语句。
相同点:
①都是Java的关键字
②都只能在类的非静态方法中使用,用来访问非静态成员方法和字段【因为静态static和对象不相关,而this和super都是和对象相关的】
③必须是构造方法的第一条于,并且不能同时存在
不同点:
①this是当前对象的引用,super相当于是子类对象中从父类继承下来部分成员的引用【但是super并没有new出对象】
②在非静态成员方法中,this用来访问自己类(本类)的方法和属性,super用来访问父类继承下来的方法和属性
③在构造方法中,this()用来调用本类的构造方法,super()用来调用父类的构造方法,而且两种调用不能同时在构造方法中出现
④构造方法中一定会存在super()的调用【用户没写编译器也会自动加】,但是this()用户没写则没有
- 代码块在继承关系下的执行顺序(初始化)
class Animal {
public String name;
public int age;
public void eat() {
System.out.println(name + "在吃饭");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public Animal() {
System.out.println("父类构造方法");
}
{
System.out.println("父类实例代码块");
}
static {
System.out.println("父类静态代码块");
}
}
class Dog extends Animal {
public Dog() {
super();
System.out.println("子类构造方法");
}
public Dog(String name, int age) {
super(name, age);
}
public void wang() {
System.out.println(name + "汪汪汪");
}
{
System.out.println("子类实例代码块");
}
static {
System.out.println("子类静态代码块");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println("===分割线====");
Dog dog2 = new Dog();
}
}
运行结果:
分析:大前提——父类优先于子类执行,静态的最先执行(而且只执行一次,因为类只被加载一次)
①父类静态代码块优先于子类静态代码块,且最早执行
②父类实例代码块和父类构造方法紧接着执行
③子类实例代码块和子类构造方法紧接着再执行
④第二次实例化子类对象的时候,父类和子类的静态代码块都不会再执行,因为类第一次已经被加载了,所以静态代码块在第一次已经执行完了。
- protected关键字
private:修饰成员变量/成员方法等,只能在当前类中使用,类外不能使用。
defalut(默认权限):包访问权限,只能在当前包中使用。
public:公开的,意味着在哪里都可以使用。
protected:受保护的——同一个包可以使用,不同包的子类可以使用。
①protected——同一个包中的同一个类
package www;
public class TestDemo1 {
protected int a = 10;
public static void main(String[] args) {
TestDemo1 testDemo1 = new TestDemo1();
System.out.println(testDemo1.a);
}
}
②protected——同一个包中的不同类
package www;
public class TestDemo2 {
public static void main(String[] args) {
TestDemo1 testDemo1 = new TestDemo1();
System.out.println(testDemo1.a);
}
}
③protected——不同包的子类【在非静态方法中通过super关键字访问】
不能之间对象名.成员名访问,因为再怎么说它也是被保护的,但是可以通过super关键字访问呢,又因为super不能在静态方法中使用,所以写一个非静态的func方法来验证被protected修饰的成员变量/成员方法可以在不同包的子类中访问。
package www2;
import www.TestDemo1;
public class TestDemo3 extends TestDemo1{
public void func() {
System.out.println(super.a);
}
public static void main(String[] args) {
// TestDemo1 testDemo1 = new TestDemo1();
// System.out.println(testDemo1.a);
}
}
问:被private修饰的成员是否可以被继承?
答:父类中private成员变量虽然在子类中不能直接访问,但是也继承到子类中了。【被继承了,只是不能访问】因为private权限出了所在类就用不了,但是被继承了,不能访问而已。
- Java的继承方式(3种)
①单继承
②多层继承
③不同类继承同一个类
在Java中不支持多继承(即一个类同时继承2个或2个以上的类)【但是C++支持多继承】。
一般我们不希望出现超过三层的继承关系,如果想从语法上进行限制继承——final关键字
- final关键字修饰
①final修饰变量:表示常量
分析:final修饰变量或字段,表示常量,常量的变量名要大小,常量不能进行修改,因此A = 20;
会报错。
②final修饰类:表示类不能被继承
分析:final修饰Animal类,则Animal类的术语叫做密封类,Animal也就不能被继承,所以Dog类在继承Animal类的时候会出现报错。
③final修饰数组
分析:语句1错误,语句2正确
final修饰的是array,所以此时array里面保存的值不能变【即 array这个引用指向的对象不能改变,但是array指向对象的内容可以改变】,即0x2不能变,但是0x2指向的对象内容{1,2,3}可以改变。 - 继承与组合
继承:对象之间是 is a 的关系
组合:对象之间是a part of / has a 的关系
组合式一种代码的实现方式,式一种设计思想。组合并没有设计到特殊的语法(例如extends这样的关键字),组合仅仅式将一个类的实例作为另一个类的字段。
例子:学校由若干名老师和若干名学生组成。【组合】
class Student {
}
class Teacher {
}
public class School {
public Student[] students= new Student[3];
public Teacher[] teachers = new Teacher[3];
public static void main(String[] args) {
School school = new School();
}
}
内存图: