11Java基础之继承
继承
- java中提供了一个extends的关键字,意思是“拓展”。用这个关键字可以让一个类和另一个类建立父子关系。
继承的特点:
-
子类能直接继承父类的非私有成员(成员变量,成员方法)
-
JAVA中类只有单继承,没有多继承。一个儿子只能有一个爸爸,一个爸爸可以有多个儿子。
-
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extend来表示。
继承后对象的创建
- 子类的对象是由子类和父类 以及多张设计图一起创建的。
- 对象能直接访问什么成员,是由父类这多张设计图共同决定的,多张设计图在外暴露了什么成员,对象就可以访问什么成员。
注意:
- 子类继承父类,就会拥有父类的全部方法。
- 父类中私有的东西,子类无法继承。
- 在IDEA工具中,父类或者继承的子类,在类名上按Ctrl+H快捷键可以看到类的继承关系。
父类:
public class A {
public int i;
public void print(){
System.out.println("我是A类");
}
private int j;
private void print1(){
System.out.println("我是A类的私有方法");
}
}
子类:
public class B extends A{
private int k;
public void print2(){
// 继承特点:子类只能继承父类的非私有成员
System.out.println(i);
print();
// 继承特点:子类不能继承父类的私有成员
// System.out.println(j); // 报错
// print1(); // 报错
}
}
main方法:
public static void main(String[] args) {
// 子类对象的创建特点:会由子类和父类等多张设计图共同创建出子类对象,但是能访问什么看权限。
B b = new B();
System.out.println(b.i);
// System.out.println(b.j); // 报错
// System.out.println(b.k); // 报错
b.print();
// b.print1(); // 报错
b.print2();
}
}
- 继承的好处就是可以减少重复代码的编写
案例:
父类:
public class People {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
子类:
public class Teacher extends People{
private String skill;
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
}
main方法:
public static void main(String[] args) {
Teacher t = new Teacher();
t.setName("波妞");
t.setSkill("程序员鼓励师,哄程序员加班~");
System.out.println(t.getName());
System.out.println(t.getSkill());
}
输出结果:
波妞
程序员鼓励师,哄程序员加班~
- teacher类继承了people类,所以它可以调用父类中的方法,也可以调用自身的方法。
继承的相关注意事项
- 权限修饰符
- 权限修饰符,是用来限制类中的成员(成员变量、成员方法、构造器、代码块……)能够被访问的范围。
- 权限修饰符有如下几种:
- private:只能本类
- 缺省:本类、同一个包中的类
- protected:本类、同一个包中的类、子孙类中
- public:任意位置
案例:
父类:
public class Father {
// 1. private,私有的,只能在本类中访问
private void privateMethod() {
System.out.println("private");
}
// 2. 缺省,只能在本类,同一包下的其他类中访问
void defaultMethod() {
System.out.println("default:缺省");
}
// 3. prodected,子类权限,只能在本类,同一个包下的类,子孙类中访问
protected void protectedMedhod() {
System.out.println("protected");
}
// 4. public,公共权限,可以在任意类中访问
public void publicMedhod() {
System.out.println("public");
}
public static void main(String[] args) {
Father f = new Father();
f.privateMethod();
f.defaultMethod();
f.protectedMedhod();
f.publicMedhod();
}
}
**同一个类中,所有的修饰的方法都可以调用。**
同一个包,不同的类调用
public class Demo01 {
public static void main(String[] args) {
Father f = new Father();
//f.privateMethod(); // 报错
f.defaultMethod();
f.protectedMedhod();
f.publicMedhod();
}
}
**除了私有修饰的方法不能调用,剩下的都可以调用。**
-------------------------------------------------
不同包的类
public class Demo02 {
public static void main(String[] args) {
Father f = new Father();
// f.privateMethod(); // 报错
// f.defaultMethod(); // 报错
// f.protectedMedhod();// 报错
f.publicMedhod();
}
}
**除了公共修饰的方法,别的修饰的方法都不能调用。**
-----------------------------------------------
不同包的子类
public class Son extends Father {
public void print(){
// privateMethod(); // 报错
// defaultMethod(); // 报错
protectedMedhod();
publicMedhod();
}
public static void main(String[] args) {
Son s = new Son();
// s.privateMethod(); // 报错
// s.defaultMethod(); // 报错
s.protectedMedhod();
s.publicMedhod();
}
}
**除了私有和缺省修饰的方法,别的都可以调用。**
单继承
- Java是单继承的,Java中的类不支持多继承,但是支持多层继承。
案例:
// 目标:搞清楚继承的特点
public class Test {
public static void main(String[] args) {
}
}
//1. Java是单继承的,一个类只能有一个父类
//2. Java不支持多继承
class A{ }
class B{ }
//class C extends A,B{} //报错
//3. Java支持多层继承
class C extends A{}
class D extends C{}
- 为何Java中类不支持多继承?
- 如果C类同时继承了A、B类,但是A、B类中都有同名方法时,C类在调用方法的时候是无法区分调用的是哪个类中的方法。
- 多层继承则不存在这个问题,假设C类继承A,D类继承C,如果A类和C类中都有同名方法,D类调用的时候只会选择C类中的方法,Java会采取就近原则。
object类
- object类是java中所有类的祖宗。我们写的任意类,其实都是object的子类或者子孙类。
方法重写
- 什么是方法重写?
- 当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是重写。
- 注意:重写后,方法的访问,Java会遵循就近原则。
方法重写的其他注意事项
- 重写小技巧:使用Override注解,他可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。
- 子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限(public > protected > 缺省)。
- 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
- 私有方法、静态方法不能被重写,如果重写会报错的。
案例:
父类:
public class Animal {
public void run(){
System.out.println("每个动物都会跑!");
}
}
子类:
public class Tiger extends Animal{
// 方法重写:名称相同,参数列表相同,返回值类型相同
@Override //重写的校验注解:安全,优雅
public void run(){
System.out.println("老虎跑的贼快!");
}
}
main方法:
public static void main(String[] args) {
Tiger t = new Tiger();
t.run();
}
输出结果:
老虎跑的贼快!
方法重写的应用场景
- 子类重写Object类的toString()方法,以便返回对象内容。
案例:
创建一个学生类:
public class Student {
private String name;
private char sex;
private double height;
private String desc;
public Student() {
}
public Student(String nanme, char sex, double height, String desc) {
this.name = nanme;
this.sex = sex;
this.height = height;
this.desc = desc;
}
public String getNanme() {
return name;
}
public void setNanme(String nanme) {
this.name = nanme;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString(){
return "Student {name = " + name + ", sex = " + sex + ", height = " + height + ", desc = " + desc + "}";
}
}
main方法:
// 目标:搞清楚方法重写在实际开发类中的应用场景。
public class Test {
public static void main(String[] args) {
Student s1 = new Student("王小二", '男', 1.78, "是个好学生");
System.out.println(s1);
}
}
输出结果:
Student {name = 王小二, sex = 男, height = 1.78, desc = 是个好学生}
在打印对象时,实际是调用了objcet类中的toString方法,输出的结果就是对象的内存地址。如果toString被重写后,在打印对象时,则调用的是重写后的toString方法。
子类中访问其他成员的特点
-
在子类方法中,访问其他成员(成员变量、成员方法),是依据就近原则。
- 先子类局部范围找。
- 然后子类成员范围找。
-
如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要在子类中使用父类的成员怎么办?
- 可以通过super关键字,指定访问父类的成员:super.父类成员变量/父类成员方法
VS this:
- 代表的对象不同:
this:本身调用者的这个对象
super:代表父类对象的应用 - 调用前提:
this:没有继承也可以使用。
super:只能在继承后使用。 - 构造方法:
this(),调用的是本类的构造。
super(),调用的是父类中的构造。
案例:
// 目标:继承过后,子类访问成员的特点,就近原则
public class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.showName();
}
}
class Zi extends Fu{
String name = "子类名称";
public void showName(){
String name = "局部名称";
System.out.println(name);
System.out.println(this.name);// 访问子类成员name
}
}
class Fu{
String name = "父类名称";
}
输出结果:
局部名称
子类名称
-------------------------------------------------------------
public class Test2 {
public static void main(String[] args) {
Zi2 z = new Zi2();
z.run();
z.go();
}
}
class Zi2 extends Fu2{
@Override
public void run(){
System.out.println("子类跑");
}
public void go(){
run(); // 调用子类的run方法
super.run();// 调用父类的run方法
}
}
class Fu2{
public void run(){
System.out.println("父类跑");
}
}
输出结果:
子类跑
子类跑
父类跑
子类构造器的特点
- 子类的全部构造器,都会先调用父类的构造器,再执行自己。
案例:
父类:
public class Animal {
public Animal(){
System.out.println("父类无参构造器");
}
public Animal(String n){
System.out.println("父类有参构造器");
}
}
子类:
public class Wolf extends Animal{
public Wolf() {
System.out.println("子类Wolf无参构造器");
}
public Wolf(String n){
System.out.println("子类Wolf有参构造器");
}
}
main方法:
//目标:搞清楚子类构造器的特点:子类全部构造器会优先调用父类的构造器,再执行子类的构造器
public class Test {
public static void main(String[] args) {
// 子类的全部构造器都会先调用父类的构造器,在执行子类的构造器
Wolf w = new Wolf();
Wolf w2 = new Wolf("wolf");
}
}
输出结果:
父类无参构造器
子类Wolf无参构造器
父类无参构造器
子类Wolf有参构造器
子类构造器是如何实现父类构造器的?
- 默认情况下,子类全部构造器的第一行代码都是super()(写不写都有),它会调用父类的无参构造器。
- 如果父类没有无参构造器,则我们必须在子类构造器的第一行手写super(……),指定去调用父类的有参构造器。
为什么子类的全部构造器会先调用父类的构造器?
案例:
// 父类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
子类:
public class Teacher extends Person{
private String skill;
public Teacher() {
}
public Teacher(String name, int age, String skill) {
super(name,age); //子类调用父类构造器:初始化继承自父类的那部分数据
this.skill = skill;
}
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
}
main方法:
public static void main(String[] args) {
Teacher t1 = new Teacher();
Teacher t2 = new Teacher("蜘蛛侠", 30, "教语文");
t1.setName("钢铁侠");
t1.setAge(20);
t1.setSkill("教数学");
System.out.println(t1.getName());
System.out.println(t1.getAge());
System.out.println(t1.getSkill());
System.out.println(t2.getName());
System.out.println(t2.getAge());
System.out.println(t2.getSkill());
}
输出结果:
钢铁侠
20
教数学
蜘蛛侠
30
教语文
- 子类构造器可以通过父类构造器,把对象中包含父类这部分的数据先初始化赋值,再回来把对象利包含子类这部分的数据也进行初始化赋值。
这样既调用了父类的构造器又调用了子类的构造器,这样就保证了数据的完整性。
this(……)调用兄弟构造器
- 任意类的构造器中,是可以通过this(……)去调用该类的其他构造器的。
案例:
学生类:
public class Student {
private String name;
private int age;
private String school;
public Student() {
}
public Student(String name, int age){
this(name, age, "霍格沃兹");
}
public Student(String name, int age, String school) {
this.name = name;
this.age = age;
this.school = school;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
main方法:
public static void main(String[] args) {
// 1. 创建对象封装数据
Student s1 = new Student("孙悟空", 500, "斜月三星洞");
System.out.println(s1.getName());
System.out.println(s1.getAge());
System.out.println(s1.getSchool());
// 不需要给学校赋值,默认值是霍格沃兹
// 注意事项:this(...),super(...)不能同时出现,且必须在构造器的第一行。
Student s2 = new Student("蜘蛛精", 300);
System.out.println(s2.getName());
System.out.println(s2.getAge());
System.out.println(s2.getSchool());
}
输出结果:
孙悟空
500
斜月三星洞
蜘蛛精
300
霍格沃兹
- 注意事项:this(...),super(...)不能同时出现,且必须在构造器的第一行。
- 如果同时出现,this(...)会调用有参构造器,有参构造器会调用父类的有参构造器,这是如果在调用super(……)就会出现重复调用父类构造器的问题。

浙公网安备 33010602011771号