1.继承
继承概念
继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
例如:狗和猫都是动物,那么我们就可以将共性的内容进行抽取,然后采用继承的思想来达到共用。
上述图示中,Dog和Cat都继承了Animal类,其中:Animal类称为父类/基类或超类,Dog和Cat可以称为Animal的子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。
继承语法
修饰符 class 子类 extends 父类 { //..... }
遵循原则
- 子类会将父类中的成员变量或者成员方法继承到子类中了
- 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了
// Animal.java
class Animal{
String name;
int age;
float wight;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep(){
System.out.println(name + "正在睡觉");
}
}
// Dog.java
class Dog extends Animal{
void bark(){
System.out.println(name + "汪汪汪~~~");
}
}
// Cat.Java
class Cat extends Animal{
void mew(){
System.out.println(name + "喵喵喵~~~");
}
}
// TestExtend.java
public class TestExtend {
public static void main(String[] args) {
Dog dog = new Dog();
// dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
System.out.println(dog.name);
System.out.println(dog.age);
// dog访问的eat()和sleep()方法也是从Animal中继承下来的
dog.eat();
dog.sleep();
dog.bark();
}
}
子类中访问父类的成员变量或成员方法
不管是成员变量还是成员方法,都有两种情况:
(1)子类和父类不存在同名
(2)子类和父类同名
在子类方法中 或者 通过子类对象访问成员或方法时:
1.如果访问的成员变量子类中有,优先访问自己的成员变量。
2.如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
3.如果访问的成员变量与父类中成员变量同名,则优先访问自己的。
成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
1.通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
2.通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;
子类构造方法(重要)
父子父子,先有父再有子,即:子类对象构造时,需要先调用父类构造方法,然后执行子类的构造方法。
原因:子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。父子父子
肯定是先有父再有子,所以在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整 。
class Animal {
public String name = "123";//姓名
public int age;
public Animal() {
System.out.println("Animal()");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
System.out.println("带有2个参数的构造方法!");
}
public void eat() {
System.out.println(name+" 正在吃饭!");
}
}
class Dog extends Animal {
public Dog() {
//super();//编译器会帮你默认加上的
System.out.println("DOG()");
}
public Dog(String name,int age) {
//super();
super(name,age);
//super("七月",10);//显示的调用父类的构造方法,帮助父类的成员进行初始化
}
public void wangwang() {
System.out.println(name +" 旺旺叫!");
}
}
class Cat extends Animal {
public Cat() {
super();
}
public int size;
public Cat(String name,int age,int size) {
super(name,age);
this.size = size;
}
public Cat(String name,int age) {
super(name,age);
//this("1",2,3);
}
public void mew() {
System.out.println(name +" 正在喵喵叫!");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println("=====");
Dog dog2 = new Dog();
}
}
- 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
- 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
2.super关键字
子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?
- super关键字,该关键字主要作用:在子类方法中访问父类的成员。
super和this
super和this都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,那他们之间有什么区别呢?
【相同点】
- 只能在类的非静态方法中使用,用来访问非静态成员方法和字段.
- 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
【不同点】
- this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
- 构造方法中一定会存在super(…)的调用,用户没有写编译器也会增加,但是this(…)用户不写则没有
3.protected 关键字
刚才我们发现, 如果把字段设为 private, 子类不能访问. 但是设成 public, 又违背了我们 “封装” 的初衷.
两全其美的办法就是 protected 关键字.
对于类的调用者来说, protected 修饰的字段和方法是不能访问的
对于类的 子类 和 同一个包的其他类 来说, protected 修饰的字段和方法是可以访问的
// 为了掩饰基类中不同访问权限在子类中的可见性,为了简单类B中就不设置成员方法了
// extend01包中
public class B {
private int a;
protected int b;
public int c;
int d;
}
// extend01包中
// 同一个包中的子类
public class D extends B{
public void method(){
// super.a = 10; // 编译报错,父类private成员在相同包子类中不可见
super.b = 20; // 父类中protected成员在相同包子类中可以直接访问
super.c = 30; // 父类中public成员在相同包子类中可以直接访问
super.d = 40; // 父类中默认访问权限修饰的成员在相同包子类中可以直接访问
}
}
// extend02包中
// 不同包中的子类
public class C extends B {
public void method(){
// super.a = 10; // 编译报错,父类中private成员在不同包子类中不可见
super.b = 20; // 父类中protected修饰的成员在不同包子类中可以直接访问
super.c = 30; // 父类中public修饰的成员在不同包子类中可以直接访问
//super.d = 40; // 父类中默认访问权限修饰的成员在不同包子类中不能直接访问
}
}
// extend02包中
// 不同包中的类
public class TestC {
public static void main(String[] args) {
C c = new C();
c.method();
// System.out.println(c.a); // 编译报错,父类中private成员在不同包其他类中不可见
// System.out.println(c.b); // 父类中protected成员在不同包其他类中不能直接访问
System.out.println(c.c); // 父类中public成员在不同包其他类中可以直接访问
// System.out.println(c.d); // 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问
}
}
4.final 关键字
final关键可以用来修饰变量、成员方法以及类,一定要初始化(相当于c语言的const)
final 关键字的功能是 限制 类被继承“限制” 这件事情意味着 “不灵活”. 在编程中, 灵活往往不见得是一件好事. 灵活可能意味着更容易出错.是用 final 修饰的类被继承的时候, 就会编译报错, 此时就可以提示我们这样的继承是有悖这个类设计的初衷的.
修饰变量或字段,表示常量(即不能修改)
修饰类:表示此类不能被继承 , 我们平时是用的 String 字符串类, 就是用 final 修饰的, 不能被继承.
修饰方法:表示该方法不能被重写
1.修饰变量或字段
final int a = 10;//使用final 一定要初始化
a = 20; // 编译出错
2.修饰类
final public class Animal {
...
}
public class Bird extends Animal {
...
}
// 编译出错
Error:(3, 27) java: 无法从最终com.bit.Animal进行继比特就业课