先来实现猫狗类
package inherit;
class Dog {
public String color;
public String name;
public void barks() {
System.out.println(name + "汪汪叫");
}
}
class Cat {
public String color;
public String name;
public void barks() {
System.out.println(name + "喵喵叫");
}
public void eat() {
System.out.println(name + "在吃饭");
}
public void catchMouse() {
System.out.println(name + "在抓老鼠");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "大黄";
dog.color = "yellow";
dog.eat();
}
}
结合上面代码会发现,两个类都有name、color成员和eat方法,代码在两个类中重复的较多,如何将这些共性抽取呢?
面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。
继承主要解决的问题是:共性的抽取,实现代码复用。
例如:狗和猫都是动物,那么我们就可以将共性的内容进行抽取,然后采用继承的思想来达到共用。
上述图示中,Dog和Cat都继承了Animal类。
其中:Animal类称为父类/基类或超类,Dog和Cat可以称为Animal的子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。
从继承概念中可以看出继承最大的作用就是:实现代码复用,还有就是来实现多态(后序讲)。
对猫狗类的重新实现
package inherit;
class Animal {
public String color;
public String name;
public void eat() {
System.out.println(name + "在吃饭");
}
}
class Dog extends Animal{
public void barks() {
System.out.println(name + "汪汪叫");
}
}
class Cat extends Animal{
public void barks() {
System.out.println(name + "喵喵叫");
}
public void catchMouse() {
System.out.println(name + "在抓老鼠");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "大黄";
dog.color = "yellow";
dog.eat();
}
}
实现一个Animal类,将Dog、Cat类相同的成员和方法放在其中。
父类里面有什么,子类就会继承什么。当子类继承父类后,子类就会有父类的成员属性和方法。
在继承体系中,子类将父类中的方法和字段继承下来了,那在子类中能否直接访问父类中继承下来的成员呢?
package inherit;
class Base {
int x;
int y;
}
public class Derived extends Base{
int z;
public void method() {
x = 10;// 访问从父类中继承下来的x
y = 20;//访问从父类中继承下来的y
z = 30;//访问子类自己的z
System.out.println(x);
System.out.println(y);
System.out.println(z);
}
public static void main(String[] args) {
Derived derived = new Derived();
derived.method();
}
}
package inherit;
class Base {
int x;//Base中的x
int y;
}
public class Derived extends Base{
int x;//Derived中的x
int z;
public void method() {
x = 100;//执行就近原则 - 子类优先
y = 20;
z = 30;
System.out.println(x);
System.out.println(y);
System.out.println(z);
}
public static void main(String[] args) {
Derived derived = new Derived();
derived.method();
Base base = new Derived();
System.out.println("Base中的x:" + base.x);
}
}
上述图片表示Base中的x没有赋值,说明当子类和父类成员变量同名时,采用就近原则,也就是子类优先。
在子类方法中 或者 通过子类对象访问成员时:
成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
那如果想要访问父类的成员该该怎么办?
使用super关键字:
public void method() {
super.x = 100;//访问父类
y = 20;
z = 30;
System.out.println(x);
System.out.println(super.x);
System.out.println(y);
System.out.println(z);
}
可以看到加上super之后,子类访问了父类的x成员。
class Base {
int x;//Base中的x
int y;
public void methodA() {
System.out.println("Base中的methodA方法");
}
}
public class Derived extends Base {
int x;//Derived中的x
int z;
public void methodB() {
System.out.println("Derived中的methodB方法");
}
public void methodC() {
methodB();//访问子类自己的methodB()
methodA();//访问父类继承的methodA()
methoD()//;//编译失败,在整个继承体系中没有发现方法Derived()
}
}
优先匹配子类,然后是父类。
总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错。
class Base {
int x;//Base中的x
int y;
public void methodA() {
System.out.println("Base中的methodA方法");
}
}
public class Derived extends Base {
int x;//Derived中的x
int z;
public void methodA() {
System.out.println("Derived中的methodA方法");
}
public void methodB() {
System.out.println("Derived中的methodB方法");
}
public void methodC() {
methodB();//访问子类自己的methodB()
methodA();//访问父类继承的methodA()
}
}
还是优先访问子类。
【说明】
当子类和父类有成员方法相同的时候,访问父类的方法该怎么办?
后续super关键字会讲到。
super 关键字的主要作用是:在子类中访问父类的成员。
super 的三个用法:
class Animals{
public String name;;
public int age;
public void eat() {
System.out.println(name + "正在吃饭");
}
}
class Dog extends Animals{
public String silly;//傻 - 狗的属性
public String name = "大黄";
public void houseGuard() {
System.out.println(super.name + "正在看家护院");
}
}
class Cat extends Animals{
public String name = "小黑";
public void catchMouse() {
System.out.println(super.name + "正在抓老鼠");
}
}
class Cat extends Animals{
public String name = "小黑";
public void catchMouse() {
System.out.println(super.name + "正在抓老鼠");
super.eat();//调用父类的普通方法 - 静态方法不可调用
eat();//直接eat()也可以
}
}
只能在非静态方法中使用。
class Animals{
public String name;;
public int age;
public Animals (String name, int age ) {
this.age = age;
this.name = name;
}
}
class Cat extends Animals{
public Boolean loveClean;//爱干净
public Cat(String name, int age, boolean loveClean) {
super(name, age);
this.loveClean = loveClean;
}
public void catchMouse() {
System.out.println(super.name + "正在抓老鼠");
}
}
class Animals{
public String name;;
public int age;
public Animals (String name, int age ) {
this.age = age;
this.name = name;
}
public void eat() {
System.out.println(name + "正在吃饭");
}
}
class Dog extends Animals{
public boolean silly;//傻 - 属性
public Dog(String name, int age, boolean silly) {
//1.先帮助父类初始化
super(name, age);//用到super关键字
this.silly = silly;
}
public void houseGuard() {
System.out.println(super.name + "正在看家护院");
}
}
class Cat extends Animals{
public Boolean loveClean;//爱干净
public Cat(String name, int age, boolean loveClean) {
super(name, age);
this.loveClean = loveClean;
}
public void catchMouse() {
System.out.println(super.name + "正在抓老鼠");
}
}
【注意】
子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整 。
共同点:
不同点:
class Person{
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("构造方法执行");
}
{
System.out.println("实例代码块执行");
}
static {
System.out.println("静态代码块执行");
}
}
public class TestDemo {
public static void main(String[] args) {
Person person1 = new Person("zhangan", 19);
System.out.println("------------------------");
Person person2 = new Person("lisi", 18);
}
}
如果是继承关系的执行顺序又会是什么呢?
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person:构造方法执行");
}
{
System.out.println("Person:实例代码块执行");
}
static {
System.out.println("Person:静态代码块执行");
}
}
class Student extends Person {
public Student(String name, int age) {
super(name, age);
System.out.println("Student:构造方法执行");
}
{
System.out.println("Student:实例代码块执行");
}
static {
System.out.println("Student:静态代码块执行");
}
}
public class TestDemo {
public static void main(String[] args) {
Student student1 = new Student("zhangsan", 18);
System.out.println("------------------");
Student student2 = new Student("zhangsan", 18);
}
}
同一个包中的不同类该如何访问?
package demo1;
public class TestDemo {
protected int a = 10;
public void func() {
System.out.println(a);
}
}
package demo1;
public class TestDemo2 {
public static void main(String[] args) {
TestDemo testDemo = new TestDemo();
System.out.println(testDemo.a);
}
}
不同包中的非子类如何访问?
package demo1;
public class TestDemo {
protected int a = 10;
public void func() {
System.out.println(a);
}
}
package demo1;
public class TestDemo2 {
public static void main(String[] args) {
TestDemo testDemo = new TestDemo();
System.out.println(testDemo.a);
}
}
package demo2;
import demo1.TestDemo;
public class Test extends TestDemo {
public void test() {
System.out.println(super.a);
}
public static void main(String[] args) {
Test t = new Test();
t.test();
}
}
java中支持的继承方式
1.单继承
class Cat {
public int age;
}
class Dog extends Cat{
public int age;
}
2.多层继承
class A {
}
class B extends A{
}
class C extends B{
}
class D extends C {
//可以一直写下去
}
3.不同类继承同一个类
class Animals {
public String color;
public int age;
}
class Wolf extends Animals{
}
class Tiger extends Animals{
}
java不支持多继承
final 可以用来修饰成员方法,变量,类。
修饰变量或者字段时表示常量,既不能被修改。
final int a = 10;
a = 20;//这里会报错,因为常量无法被修改
表示这个类不能被继承
final class Plant {
}
public class Animal extends Plant{
}
编译器提示无法继承。
组合与继承类似,两者都可以起到代码复用的效果,只是组合的使用的情况更多。
组合表示的是部分的关系。
比如显示屏、CPU、键盘是计算机的一部分。
class Keyboard {
}
class Display {
}
class CPU {
}
public class Computer {
//属性或者是字段
public Keyboard[] keyboards;
public Display[] displays;
public CPU[] cpu;
}
每一个类都是Computer的一部分,并且都可以拿出来使用。