内容摘要:
类与类间存在三种关系:
组合:如果自定义类型A中的属性值的类型是另一个自定义类型B时,A与B之间就是组合关系.
继承:暂未学习
代理:暂未学习
案例:
// Person和Pet是组合关系
public class Person {
String name;
Pet pet;
}
public class Pet {
String name;
String color;
}
概念:
把多个类中相同的成员提取出来定义到一个独立的类中,然后让这些类和独立的类产生一个关系后,都能直接使用独立类中的成员,这个关系就是继承.
格式:
class 子类名 extends 父类名 {}
extends:代表继承的关键字
好处:
提高代码的复用性;
提高了代码的维护性(修改父类中方法,所有的子类均不用修改);
继承是多态的前提.
坏处:
增强了类与类间的耦合性(开发时遵循原则:低耦合,高内聚).
特点:
Java语言只支持单继承,不支持多继承;
Java语言支持多层继承,不支持循环继承.
何时使用继承:
采用假设法,继承遵循”is a”的原则(A类是B类的一种,才能使用继承).
注意事项:
子类只能访问父类中的非私有的成员(成员变量和成员方法);
不能为获取某一个功能而去继承一个类;
子类没有继承父类的构造方法,当可以通过super()调用父类的无参构造,super(参数)调用父类的有参构造.
案例:
package com.itheima.extendsreview;
/*
* 继承案例
*/
public class ExtendsDemo {
public static void main(String[] args) {
// 创建子类对象,子类可以访问父类的非私有的成员
Teacher t = new Teacher("张三", 16);
t.age = 20; // 可以访问父类非私有的成员变量
//t.name = "李四"; // 子类不能访问父类的私有的成员变量
t.setName("李四"); // 子类可以通过成员变量的公共的访问方法访问父类的私有成员变量
t.showInfo(); // 子类可以访问父类的非私有的成员方法
//t.work(); // 子类不能访问父类的非私有的成员方法
}
}
class Person {
private String name;
public int age;
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 公共的方法
public void showInfo() {
System.out.println(this.name + "..." + this.age);
}
// 私有的方法
private void work() {
System.out.println("我学习");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Teacher extends Person {
// 子类不能继承父类的构造方法,构造方法是父类进行数据初始化的,
// 如果子类继承后,子类没有对象的属性值,则有参构造会报错
public Teacher() {}
// 子类可以通过super关键字调用父类的构造方法,通过父类初始化属性值
public Teacher(String name, int age) {
super(name, age);
}
}
类的组成:
成员变量
构造方法
成员变量
继承中成员变量关系:
1: 当子类的成员方法调用变量遵循就近原则(查询变量遵循的顺序):
本方法中局部变量–>本类中的成员变量–>父类的成员变量
2: 当子类的成员方法中局部变量,子类的成员变量,父类的成员变量三者同名时:
该成员方法调用变量时:
(1) 变量名:调用本方法中的局部变量;
(2) this.变量名:调用本类中成员变量(this代表当前对象的引用);
(3) super.变量名:调用父类中的成员变量(super代表父类存储空间的标识).
继承中构造方法的关系:
遵循规则:子类创建对象时,必须先将父类进行加载并进行初始化.
1:子类的每个构造方法的都默认调用父类的无参构造方法,即隐含super()语句,原因如下:
创建子类对象时需要先将父类进行初始化,子类才能获取父类的成员;
2:子类的构造方法可以同通过this(…)调用其他的构造方法,但至少有一个构造方法要调用父类的构造方法(父类需要进行初始化);
3:子父类构造方法执行顺序:可以看作是创建子类对象时调用子类的构造方法,子类构造方法执行过程中调用父类的构造方法(子类完成初始化前将父类进行初始化);
4:子类的构造方法的第一条有效语句必须是this(…)调用其他构造方法的语句,或者是super(…)调用父类构造方法的语句,二者只能出现一个;
5:当父类中没有空参构造时,需要通过调用父类的有参构造进行父类的初始化.
继承中成员方法的关系:
1:子类只能访问父类非私有的成员方法;
2:子类成员方法和父类非私有成员方法不同名时,这两个方法是不同的方法,子类均可访问;
3:子类成员方法和父类非私有成员方法同名时:
(1):方法声明完全相同(返回值,参数列表):这两个方法是重写关系,子类方法将父类方法覆盖;
(2):方法声明不完全相同[返回值不同,参数列表不同(参数个数,参数类型,类型对应的顺序)]:这两个方法是重载,子类均可访问;
4:子类成员方法和父类私有的成员方法同名时:这两个方法时各自类所独有,没有关系,子类不能访问父类的该成员方法.
案例:
package com.itheima.extendsreview;
/*
* 继承体系中子父类的成员关系
*/
public class ExtendsDemo2 {
public static void main(String[] args) {
/*
* 成员变量的关系
*/
Son s = new Son();
// A:子类的成员方法调用变量遵循就近原则
// (查询变量定义的顺序):子类局部方法-->子类成员变量-->父类非私有成员变量
s.show(); // 输出:age:1,age:10,age:20
// B:当子类的成员方法中局部变量,子类的成员变量,父类的成员变量三者同名时:
// 变量名:调用本方法中的局部变量
// this.变量名:调用本类中成员变量(this代表当前对象的引用)
// super.变量名:调用父类中的成员变量(super代表父类对象存储空间的标识)
s.show2(); // 输出:age:1,age:11,age:22
/*
* 构造方法的关系
* 遵循规则:子类创建对象时,必须先将父类进行加载并进行初始化.
*/
// A:子类不能继承父类的构造方法
// 子类不能继承父类的构造方法,构造方法是父类进行数据初始化的,
// 如果子类继承后,子类没有对象的属性值,则有参构造会报错
// B:子类的每一个构造方法都会默认的调用父类的无参构造方法
Son ss = new Son(); // 输出:我是父类的空参构造,我是子类的空参构造
// C:子类的构造方法可以同通过this(...)调用子类其他的构造方法,
// 但至少有一个构造方法要调用父类的构造方法(父类需要进行初始化);
// D:子类的构造方法的第一条有效语句必须是this(...)调用其他构造方法的语句,
// 或者是super(...)调用父类构造方法的语句,二者只能出现一个;
// E:当父类中没有空参构造时,子类需要显示的调用父类的有参构造进行父类的初始化.
/*
* 成员方法的关系:
*/
// A:子类只能访问父类非私有的成员方法;
// B:子类成员方法和父类非私有成员方法不同名时,这两个方法是不同的方法,子类均可访问;
// C:子类成员方法和父类非私有成员方法同名时:
// 方法声明完全相同(返回值,参数列表):这两个方法是重写关系,子类方法将父类方法覆盖;
ss.show(); // 输出:age:1,age:10,age:20
// 方法声明不完全相同[返回值不同,参数列表不同(参数个数,参数类型,类型对应的顺序)]:这两个方法是重载,子类均可访问;
ss.show3(); // 输出:你好
ss.show3("张三"); // 输出:name:张三
// D:子类成员方法和父类私有的成员方法同名时:这两个方法时各自类所独有,没有关系,子类不能访问父类的该成员方法.
// ss.work(); // 不能访问,私有的成员方法只能在本类中访问,测试类中不能访问
}
}
class Father {
private String name;
public int age = 22;
public int age2 = 20;
public Father() {
System.out.println("我是父类的空参构造");
}
public Father(String name, int age) {
this.name = name;
this.age = age;
System.out.println("我是父类的有参构造");
}
// 公共的方法
public void showInfo() {
System.out.println(this.name + "..." + this.age);
}
public void show() {
System.out.println(this.name + "..." + this.age);
}
public void show3() {
System.out.println("你好");
}
// 私有的方法
private void work() {
System.out.println("Father学习");
}
}
class Son extends Father {
public int age = 11;
public int age1 = 10;
public Son() {
System.out.println("我是子类的空参构造");
}
public Son(int age) {
this.age = age;
System.out.println("我是子类的空参构造");
}
public void show() {
int age = 1;
System.out.println("age:"+age);
System.out.println("age:"+age1);
System.out.println("age:"+age2);
}
public void show2() {
int age = 1;
System.out.println("age:"+age);
System.out.println("age:"+this.age);
System.out.println("age:"+super.age);
}
public void show3(String name) {
System.out.println("name:"+name);
}
// 私有的方法
private void work() {
System.out.println("Son学习");
}
}
概念:
子父类中出现了声明完全相同(方法名,返回值,参数列表)的方法,则称为子类成员方法重写(覆盖)了父类成员的方法.
特点:
1:方法重写的前提是存在继承关系的类;
2:子类重写父类的方法的修饰权限不能小于父类该方法的权限(public > 默认);
3:父类中私有成员方法不能被重写;
4:重写的方法一般加上@Override注解,该注解表示该方法必须是重写父类的方法,否则会报错.
作用:
1:可以在父类成员方法的功能基础上,增加自身的处理逻辑,增强父类该方法的功能(需要子类在重写该方法时首先调用父类的该方法);
2:覆盖父类该方法的所有逻辑,重新定义子类的处理逻辑,将父类的功能全部舍弃.
方法的重写与重载的区别:
1:重写(override):在两个存在继承关系的类中,方法声明完全相同;
2:重载(overload):在同一个类中,方法名相同,参数列表不同(参数个数,参数类型,类型对应的顺序),与返回值无关.
案例:
package com.itheima.extendsreview;
/*
* 方法重写案例
*/
public class MethodOverrideDemo {
public static void main(String[] args) {
// A:可以在父类成员方法的功能基础上,增加自身的处理逻辑,增强父类该方法的功能(需要子类在重写该方法时首先调用父类的该方法);
Cat c = new Cat();
c.show1(); // 输出:Animal show1,Cat show1
// B:覆盖父类该方法的所有逻辑,重新定义子类的处理逻辑,将父类的功能全部舍弃.
c.show2(); // 输出:Cat show2
}
}
class Animal {
public void show1() {
System.out.println("Animal show1");
}
public void show2() {
System.out.println("Animal show2");
}
public final void show3() {
System.out.println("Animal show3");
}
}
class Cat extends Animal {
// 报错,子类重写父类的方法的修饰权限不能小于父类该方法的权限(public > 默认);
/*void show1() {
System.out.println("Cat show1");
}*/
public void show1() {
super.show1();
System.out.println("Cat show1");
}
public void show2() {
System.out.println("Cat show2");
}
// 报错,因为父类的show3()方法是final修饰,不能被重写
/*public final void show3() {
System.out.println("Cat show3");
}*/
}
this关键字:
是代表当前对象的引用;
super关键字:
代表父类存储空间在子类对象中标识,相当于对象的引用,但不是对象的引用;
注意事项:
子类构造方法中通过super调用父类的构造方法,只是表示子类创建对象完成前需要对父类进行初始化(表象),本质是在加载子类.class文件前先加载父类的.class文件,并且只是在子类对象空间(使用this标识)中为父类开辟了一块存储空间,这块空间使用super进行标识.
this与super的区别:
1: this是当前对象的引用,super只是父类存储空间的标识,在子类中调用super只是为父类初始化,不会创建父类对象,父类初始化的空间是在子类对象存储空间内;
2:访问成员变量:
this.成员变量–>代表访问本类的成员变量
super.成员变量–>代表访问父类非私有的成员变量
3:访问构造方法:
this(…)–>代表访问本类的其他的构造方法
super(…)–>代表访问父类的构造方法
4:访问成员方法:
this.成员方法–>代表访问本类的成员方法
super.成员方法–>代表访问父类非私有的成员方法
案例:
package com.itheima.extendsreview;
/*
* this和super关键字的案例
*/
public class Z extends X {
Y y = new Y();
public Z() {
super();
System.out.print("Z");
}
// 主方法
public static void main(String[] args) {
new Z(); // 输出结果为:YXYZ
// 结果分析:类的初始化是分层初始化的
// 1.new Z()是创建Z的对象,创建Z对象前先加载父类X
// 2.X类中定义了Y b = new Y(),则在加载X前,先加载类Y
// 3.Y类中初始化时,调用无参构造方法,打印了"Y",Y类初始化完后返回到X类中
// 4.X中的构造方法执行,打印了"X",X类初始化完后返回到Z类中
// 5.Z类中定义Y y = new Y(),则进行Y类的初始化,打印"Y",然后执行Z类的构造,打印"Z"
}
}
class X {
Y b = new Y();
public X() {
System.out.print("X");
}
}
class Y {
public Y() {
System.out.print("Y");
}
}