一、继承
1.1 概念
当我们定义一个狗类和一个猫类时,会发现它们它们有共同的变量或者方法时,可以将它们之中的共性抽取出来,定义一个动物类,实现代码复用。
它就允许程序员在原来类的基础上进行拓展,增加新功能,这样产生的类叫做派生类。
例如:
class Animal{
public String name;
public int age;
public void eat(){
System.out.println(name+"吃饭!");
}
}
class Dog extends Animal{
public String color;
public Dog(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public void bark(){
System.out.println(name+"汪汪汪");
}
}
class Cat extends Animal{
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(name+"吃饭!");
}
public void bark(){
System.out.println(name+"喵喵喵");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("大黄",2,"黄色");
Cat cat = new Cat("小花",3);
dog.eat();
cat.eat();
}
}
语法:
修饰符 class 子类 extends 父类{
}
注:
子类会将父类的成员方法和成员变量继承到子类中。子类必须要与父类不同,不然就没必要继承了。
1.2 子类中访问父类的成员变量
1.2.1 子类和父类不存在同名的变量
class Base{
public int a = 1;
public int b = 2;
}
class Derived extends Base{
public int c = 3;
public int d = 4;
public void test(){
System.out.println(this.a);
System.out.println(this.b);
System.out.println(this.c);
System.out.println(this.d);
}
}
public class Test2 {
public static void main(String[] args) {
Derived derived = new Derived();
derived.test();
}
}
1.2.2 子类和父类存在同名的变量
class Base{
public int a = 1;
public int b = 2;
}
class Derived extends Base{
public int a = 3;
public int d = 4;
public void test(){
System.out.println(super.a);
System.out.println(this.a);
System.out.println(this.b);
System.out.println(this.d);
}
}
public class Test2 {
public static void main(String[] args) {
Derived derived = new Derived();
derived.test();
}
}
子类和父类之中都有a,但访问的结果是子类的a,因为会优先访问子类的,如果要访问父类,就可以使用super关键字(指的是子类从父类继承下来的那一块空间的地址)。
1.3 子类中访问父类的成员方法
1.3.1 成员方法名不同时
成员方法名不同时,就直接访问。
class Base{
public int a = 1;
public int b = 2;
public void methodA(){
System.out.println("Base.methodA");
}
}
class Derived extends Base{
public int a = 3;
public int d = 4;
public void methodB(){
System.out.println("Base.methodB");
}
}
public class Test2 {
public static void main(String[] args) {
Derived derived = new Derived();
derived.methodA();
derived.methodB();
}
}
1.3.2 成员方法名相同时
class Base{
public void method(){
System.out.println("Base.method");
}
}
class Derived extends Base{
public void method(){
System.out.println("Base.method");
}
}
public class Test2 {
public static void main(String[] args) {
Derived derived = new Derived();
derived.test();
derived.method();
}
}
总结:
- 在子类中访问与父类成员方法名相同的方法时,如果父类和子类同名方法的参数列表不同(重载),那么就根据方法传递的参数来选择合适的方法;如果父类和子类的同名方法都是无参,那么就是优先访问子类的方法。
- 如果非要调用父类的方法,就使用super.方法/成员变量名。
- super关键字只能在非静态方法中使用,在子类方法中,访问父类的成员变量和方法。
1.4 子类的构造方法
子类对象构造时,需要先帮助父类的成员进行初始化(调用父类的构造方法)。
class Animal{
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Dog extends Animal {
public String color;
public Dog(String name, int age, String color) {
super(name, age);//显式的调用父类的构造函数,来初始化此时子类继承过来的父类的属性。
this.color = color;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
}
public class Test1 {
public static void main(String[] args) {
Dog dog = new Dog("大黄",10,"黄色");
System.out.println(dog);
}
}
注:
- 当子类和父类都没有写构造方法时,编译器会默认提供子类不带参数的构造方法并且调用父类的构造方法。
- 如果父类中有含有参数的构造方法时,编译器就不会提供磨人的构造方法,此时就需要自己提供与父类构造方法的参数相同的super方法来显式调用父类的构造方法。
- 子类的构造方法中需要使用super调用父类的构造方法时,super只能放在第一行。
- super只能在子类的构造方法中出现一次,且不能和this同时出现。
1.5 this和super
相同点:
- 都是Java的关键字,都是只能在类的非静态方法中使用,用来访问非静态成员方法和字段。
- 在构造方法中使用时,必须是构造方法中的第一条语句,并且不能同时存在。
不同点:
- this是对当前对象的引用,用来访问本类的方法和属性;super相当于是子类对象中从父类继承下来的那部分成员的引用,用来访问父类继承下来的方法和属性。
- this是非静态成员方法的一个隐藏的参数,super不是。
- 在构造方法中:this(...)用于调用本类的构造方法,super(....)用于调用父类的构造方法,且不能同时在构造方法中出现。
- 构造方法中没有super(...)编译器会默认提供,但this(...)不写就没有。
- 初始化的顺序,思考下面代码:
class Animal{
public String name;
public int age;
static {
System.out.println("Animal的静态代码块");
}
{
System.out.println("Animal的实例代码块");
}
public Animal(){
System.out.println("Animal不带参数的构造方法");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Dog extends Animal {
public String color;
static {
System.out.println("Dog的静态代码块");
}
{
System.out.println("Dog的实例代码块");
}
public Dog(){
System.out.println("Dog不带参数的构造方法");
}
public Dog(String name, int age, String color) {
super(name, age);//显式的调用父类的构造函数,来初始化此时子类继承过来的父类的属性。
this.color = color;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
}
public class Test1 {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println("-------------------------------");
Dog dog1 = new Dog();
}
}
运行结果:
结论:
- 父类的静态代码块优先于子类的静态代码块执行,且是最早执行的,只执行一次。
- 父类的实例代码块和父类的构造方法紧接着执行。
- 子类的实例代码块和子类的构造方法紧接着执行。
- 第二次实例化子类对象时,父类和子类的静态代码块都不会再执行。
1.6 protected关键字
再一次思考这个:
- 用protected修饰就是受保护的,不同包中的子类可以使用,不同包中的非子类不能使用。
- 父类的private成员变量虽然在子类中不能直接访问,但是也继承到子类中了。
1.7 继承的方式
例如动物的继承可以有以下的:
但是在Java中只能有以下的继承:
注:
- 当你的一个类不想再被继承的时候,那么就让这个类被final修饰就好了。
- final可以修饰变量或字段,表示常量(不能修改)。
- 当final修饰方法时,表示该方法不能被重写。
-
继承与组合
组合是一种表达类之间关系的一种方式,仅仅是将一个类的实例作为另一个类的字段。组合和继承都能实现对代码的复用,但是我们更多的是使用组合。
像学生和老师等我们可以组合成一个学校,这就叫做组合:
class Student{
}
class Teacher{
}
class School{
private Student[] students;
private Teacher[] teachers;
}
public class Test2 {
public static void main(String[] args) {
}
}