一、继承
继承 (inheritance) 机制 :是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性 的基础上进行扩展,增加新功能 ,这样产生新的类,称 派生类 。继承呈现了面向对象程序设计的层次结构, 体现了 由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
(1)继承的语法
注意:1. 子类会将父类中的成员变量或者成员方法继承到子类中了2. 子类继承父类之后,要新添加自己的成员(一般是与其他子类不同的),便于区别。
继承是一种思想。对共性进行提取,减少代码的冗余,达到了代码复用的效果。
例如:
(2)父类成员的访问
1.子类中访问父类的 成员变量
1)访问子类、父类成员变量 不同名
不同名直接访问 变量名或者 this.变量名
class Base{ public int a; public int b; } //派生类(子类) class Derived extends Base{ public int c; public void method(){ //访问从父类继承的a和b a = 1; b = 2; //访问子类自身的c c = 3; } }
2)访问 子类、父类成员变量 同名
同名的情况下:(就近原则:优先子类自己)
当子类有该成员变量,优先访问子类自己的成员变量。
当子类当中不存在该成员变量,则访问父类继承下的。如果父类也没有,则编译报错
//基类(父类) class Base{ public int a = 10; public int b = 20; } //派生类(子类) class Derived extends Base{ public int a = 1; public void method(){ //访问从父类继承的a和b System.out.println("a = "+a);//1 System.out.println("b = "+b);//20 } } public class Test2 { public static void main(String[] args) { Derived derived = new Derived(); derived.method(); } }
如果要访问父类的成员变量,使用 super.变量名
2.子类中访问父类的 成员方法
1)访问 子类、父类成员方法 不同名
不同名,优先在子类中找,找到则访问,否则在父类中找,找到 则访问,否则编译报错。
class Base{ public void method(){ System.out.println("Base:父类成员方法!"); } } class Derived extends Base{ public void method2(){ System.out.println("Derived:子类成员方法!"); } public void test(){ method(); method2(); } } public class Test2 { public static void main(String[] args) { Derived derived = new Derived(); derived.test(); } }
2)访问 子类、父类成员方法 同名
同名,优先访问子类。子类没有,在父类继承的方法中找,父类没有,编译报错
class Base{ public void method(){ System.out.println("Base:父类成员方法!"); } } class Derived extends Base{ public void method(){ System.out.println("Derived:子类成员方法!"); } public void method2(){ System.out.println("Derived:子类成员方法!"); } public void test(){ method(); method2(); } } public class Test2 { public static void main(String[] args) { Derived derived = new Derived(); derived.test(); } }
如果要访问父类方法,使用super.method
3.子类构造方法
子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。
也就是说,子类在构造完成之前,一定要先帮助父类初始化
package demo1;
class Animal{
public String name;
public int age;
public void eat(){
System.out.println(this.name+"正在吃饭~~");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
// public Animal(){
//
// }
}
//继承
//extends 拓展,扩展 继承
class Dog extends Animal{
// public Dog(){
// super();
// }
public Dog(String name, int age){
//调用父类构造方法
super(name,age);//帮助初始化 子类从父类继承过来的成员,并不会 生成父类对象
System.out.println("Dog调用父类构造方法");
//super("小黑",3);
}
public void bark(){
System.out.println(this.name+"旺旺叫~~");
}
}
class Cat extends Animal{
public void miaomiao(){
System.out.println(this.name+"喵喵叫~~");
}
public Cat(String name, int age) {
super(name, age);
System.out.println("Cat调用父类构造方法");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("小黑",3);
dog.eat();
dog.bark();
System.out.println("==========");
Cat cat = new Cat("小白",4);
cat.eat();
cat.miaomiao();
}
}
1) 当给父类Animal提供了一个带参数的构造方法时,子类报错了
就是因为,在调用子类构造方法时,得先初始化父类变量,这样就不会报错了
2)不带参数的构造方法时,没有报错。而且之前没写构造方法的时候,也没报错
那是因为,没写的时候,默认会生成一个子类调用父类的构造方法
注意:同this一样,只能调用1次。而且必须得放在构造语句的第一行
否则会像这样报错
要向打印 ,可以放在super语句的后面
然后主函数调用写好的继承
4.super和this的小总结
共性:super.成员变量super.成员方法super()调用父类构造方法**针对当前对象的父类
this.成员变量this.成员方法this()调用父类构造方法**针对当前对象
不同:1. this 是当前对象的引用,当前对象即调用实例方法的对象, super 相当于是子类对象中从父类继承下来部分成员的引用2. 在非静态成员方法中, this 用来访问本类的方法和属性, super 用来访问父类继承下来的方法和属性3. 在构造方法中: this(...) 用于调用本类构造方法, super(...) 用于调用父类构造方法,两种调用不能同时在构造 方法中出现4. 构造方法中一定会存在 super(...) 的调用,用户没有写编译器也会增加,但是 this(...) 用户不写则没有
二、再谈初始化 - 观察代码块执行顺序
package demo1;
class Animal{
public String name;
public int age;
public void eat(){
System.out.println(this.name+"正在吃饭~~");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
System.out.println("父类:构造方法");
}
// public Animal(){
//
// }
static {
System.out.println("父类:静态代码块");
}
{
System.out.println("父类:实例化代码块");
}
}
//继承
//extends 拓展,扩展 继承
class Dog extends Animal{
// public Dog(){
// super();
// }
public Dog(String name, int age){
//调用父类构造方法
super(name,age);//帮助初始化 子类从父类继承过来的成员,并不会 生成父类对象
System.out.println("子类:构造方法");
//super("小黑",3);
}
public void bark(){
System.out.println(this.name+"旺旺叫~~");
}
static {
System.out.println("子类:静态代码块");
}
{
System.out.println("子类:实例化代码块");
}
}
class Cat extends Animal{
public void miaomiao(){
System.out.println(this.name+"喵喵叫~~");
}
public Cat(String name, int age) {
super(name, age);
System.out.println("Cat调用父类构造方法");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("小黑",3);
}
}
通过分析执行结果,得出以下结论:
1、父类静态代码块 优先于 子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
当再实例化一个对象时,发现静态代码块没有被执行
因为静态代码块只会执行一次
三、protect 关键字
protected常用在继承中
1.protected同一包中同一类可以访问
2.protected同一包中不一类可以访问
3.protected不同一包的子类可以访问。(这个继承的类必须时public访问权限)
4.注意:不能定义类的时候加Protected、Private
Java中不能这样写,语法不支持
四、继承方式
以动物为例,可以有很多继承,橘猫可以继承中华田园猫,中华田园猫可以继承猫,猫可以继承动物,动物也可以继续继承..
但是越往后继承,代码就会越复杂。 一般我们不希望出现超过三层的继承关系.
1.继承方式 - Java中不支持多继承。
2.fifinal 关键字
fifinal关键可以用来修饰变量、成员方法以及类。当继承层次过多,不希望再继承的时候加上final, 代表当前类或者变量等,不能继承了
1)修饰变量 ,该变量就变成常量了,只能被初始化一次
只能初始化一次,这两种都可以
2)修饰方法,表示当前该方法不能被继承了
所以这里继承的时候报错了
五、继承与组合
和继承类似, 组合也是一种表达类之间关系的方式 , 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法 (诸如 extends 这样的关键字 ), 仅仅是将一个类的实例作为另外一个类的字段。继承表示对象之间是 is-a 的关系 ,比如:狗是动物,猫是动物继承抽取共性组合表示对象之间是 has-a 的关系 ,比如:学校组合将所有类合并到一起复用
组合
class Student{
}
class Teacher{
}
class Classroom{
}
class School{
//组合,可以复用这些属性和方法
public Classroom classroom;
public Student[] students;//默认null
public Teacher[] teachers;
public int a;
public School(){
this.students = new Student[10];
this.teachers = new Teacher[10];
}
}