一、为什么需要继承
现实世界错综复杂,但是事物之间也是有关联的
举个例子,先来看小猫小狗的类
class Dog{
public String name;
public int age;
public String color;
public void eat(){
System.out.println(this.name+"吃饭");
}
public void bark(){
System.out.println(this.name+"汪汪");
}
}
class Cat{
public String name;
public int age;
public String color;
public void eat(){
System.out.println(this.name+"吃饭");
}
public void miaomiao(){
System.out.println(this.name+"喵喵");
}
}
public class test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "阿黄";
dog.age = 1;
dog.color = "白色";
dog.eat();
dog.bark();
System.out.println("==============");
Cat cat = new Cat();
cat.name = "啊白";
cat.age = 1;
cat.color = "黑色";
cat.eat();
cat.miaomiao();
}
}
小猫小狗都有共同的属性和行为,如名字、年龄、颜色、吃饭等,但是也有不同的行为,如叫声。
因此我们可以创建一个动物类,包含了两者的共同属性,之后猫狗类通过继承动物类得到它们共有的属性和行为。
class Animal{
public String name;
public int age;
public String color;
public Animal(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public void eat(){
System.out.println(this.name+"吃饭");
}
}
//使用extends来继承
class Dog extends Animal{
public void bark(){
System.out.println(this.name+"汪汪");
}
}
class Cat extends Animal{
public Cat(String name, int age, String color) {
super(name, age, color);
}
public void bark(){
System.out.println(this.name+"喵喵");
}
}
public class test {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(dog.name);
System.out.println(dog.age);
dog.eat();
dog.bark();
}
}
二、继承的概念
简单来说就是共性的抽取,实现代码复用。
上述的小猫小狗类就是子类又称派生类
动物类就叫父类又称基类,超类。
可以当成is - a的关系
小猫(小狗)是一个小动物
三、继承的语法
修饰符 class 子类 extends 父类{
//。。。。
}
如:
class Dog extends Animal
class Cat extends Animal
四、父类成员的访问
1、子类怎么访问父类成员变量和方法:同名、不同名
(1)不同名
先在子类里找,找不到到父类里找,再找不到程序出错
(2)同名
优先访问子类,如果需要访问父类的成员变量需要用super关键字
访问成员:
class Base{
public int a;
public int b;
}
class Derived extends Base{
public int c;
public int a;
public void test(){
System.out.println(this.a);//子类,this包括子类和父类
System.out.println(b);//子类没有就会访问父类
System.out.println(c);//如果子类有优先访问子类
//如果父类也没有就会报错
//如果想访问父类的a,用super.成员
System.out.println(super.a);//父类对象的引用
//super只是一个关键字,提高了代码的可读性
//不是引用,因为没有实例化对象,应理解成关键字
}
}
public class test1 {
public static void main(String[] args) {
Derived derived = new Derived();
derived.test();
}
}
访问方法:
class Base2{
public void testA(){
System.out.println("testA()");
}
}
class Derived2 extends Base2{
public void testB(){
System.out.println("testB()");
}
public void testA(){
System.out.println("testA()");
}
public void testC(){
this.testA();
super.testA();
this.testB();
}
}
public class test2 {
public static void main(String[] args) {
Derived2 derived2 = new Derived2();
derived2.testC();
//静态方法里不能使用super和this
}
}
五、继承的构造方法
子类要先帮助父类初始化成员变量,再给自己初始化
如果没有构造方法,系统会有默认的构造方法,如果自己提供了构造方法,就得调用自己的构造方法
class Animal{
public String name;
public int age;
public String color;
public Animal(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public void eat(){
System.out.println(this.name+"吃饭");
}
}
class Dog extends Animal{
//调用构造方法
public Dog(){
super("大黄",10,"白色");//必须在第一行所以this和super不能同时在第一行
}
//快捷键
//generate -》 constructor
public Dog(String name, int age, String color) {
super(name, age, color);
//并没有产生父类对象
//只是帮你初始化父类成员
}
//如果父类没有构造方法,就有默认的构造方法。如果父类有构造方法就不会提供,只能调用你写的
public void bark(){
System.out.println(this.name+"汪汪");
}
}
class Cat extends Animal{
public Cat(){
super("小蓝",19,"蓝色");
}
public Cat(String name, int age, String color) {
super(name, age, color);
}
public void miaomiao(){
System.out.println(this.name+"喵喵");
}
}
public class test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
dog.bark();
System.out.println("==============");
Cat cat = new Cat();
cat.eat();
cat.miaomiao();
}
}
super()用户不写也有,前提父类的构造方法是不带参数的,如果带参数了,就得用super( , , )
this()用户不写就没有
六、super和this
1、super和this的关系
2、super和this的区别
相同:
1、都只能在非静态方法中使用,用来访问非静态成员方法和字段
2、在构造方法中调用时,必须是构造方法中的第一条语句
不相同:
1、super只是一个关键字,提高了代码的可读性,不是引用,因为没有实例化对象,所以应理解成关键字 ,即this有对象,super无对象。
2、this.data this.func() this()用于调用本类构造方法
3、super.data super.func() super()用于调用父类构造方法
4、this()和super()不能同时出现在构造方法的第一行
七、再谈初始化(继承)
class Animal{
public String name;
public int age;
public String color;
public Animal(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
System.out.println("父类类构造方法");
}
{
System.out.println("实例化代码块(父类)");
}
static{
System.out.println("静态代码块(父类)");
}
public void eat(){
System.out.println(this.name+"吃饭");
}
}
class Cat extends Animal{
{
System.out.println("实例化代码块(子类)");
}
static{
System.out.println("静态代码块(子类)");
}
public Cat(){
super("小黄",10,"白色");
System.out.println("子类构造方法");
}
public Cat(String name, int age, String color) {
super(name, age, color);
System.out.println("子类构造方法");
}
public void bark(){
System.out.println(this.name+"喵喵");
}
}
public class test {
public static void main(String[] args){
Cat cat = new Cat();
System.out.println("==============");
Cat cat2 = new Cat();
}
}
运行结果
总结:
1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
八、protected关键字
之前讲过包,protected不仅可以在同包的同类或不同类中使用,还可以在不同包中的子类使用,是一种温和的中间方式。
举例:
先来创建两个不同的包
第一个包是Demo2
package Demo2;
public class test3 {
public int a = 0;
protected int c = 99;
public void func(){
System.out.println(c);
}
}
第二个包是Demo3,并且导入类test3,testDemo类继承test3的类,使用super.来接收父类的成员
package Demo3;
import Demo2.test3;
public class testDemo extends test3{
public void func()
{
System.out.println(super.c);//static里不能用super
}//protected成员变量可以在不同包中的子类使用,用super接收父类
public static void main(String[] args) {
testDemo test = new testDemo();
test.func();
}
}
注意:test3类必须是public修饰,如果不加就是包访问权限,不能在不同包使用,修饰类要么用public要么不加修饰。
那么什么时候用什么关键字来修饰呢?
类内部自己用:private
类调用者用:包访问权限(default)
子类用:protected
要思考过后再决定用什么。
九、继承方式
1、单继承
2、多层继承
3、不同类继承同一类
但是不允许同一类继承多个父类
为了避免这一情况,可以使用final关键字
final修饰继承类意味当前类不可以被继承,这个类叫密封类。
如果final修饰变量或字段,这个变量就成为了常量,不能被修改
如果final修饰方法表示该方法不能被重写(后序介绍)
十、继承与组合
组合是代码层次上的一种写法
和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。
组合并没有涉及到特殊的语法 (诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的成员。
继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物
组合表示对象之间是has-a的关系,比如:汽车有轮胎、发动机、方向盘等
class Teacher{
}
class Student{
}
class School{
public Teacher[] teachers;
public Student[] students;
}
组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。