目录
继承
一、基本介绍
二、示意图
三、基本语法
四、入门案例
父类
子类1
子类2
main方法
五、继承细节
第一条
第二条
第三条
第四条
编辑 第五条
第六条
第七条
第八条
第九条
第十条
六、继承本质
七、练习题
第三题
继承可以解决代码冗余过高的问题,将两个或多个类中相同的属性和方法提取出来,放在一个类中,称其为父类,子类继承父类,就是继承了这些属性和方法(不需要再次说明),同时,子类中也可以有其特有的属性和方法
子类 extends 父类{
子类特有的属性和发方法
}
注意事项:子类中一定要加关键字extends
package com.hspedu.extend_;
public class Student {//父类
public String name;
public int age;
private double score;
public void setScore(double score) {
this.score = score;
}
public void showInfo(){
System.out.println("学生名 " + name + " 年龄 " + age + " 成绩 " + score);
}
}
package com.hspedu.extend_;
public class Pupil extends Student{
public void testing() {
System.out.println("小学生 " + name + " 正在考小学数学..");
}
}
package com.hspedu.extend_;
public class Graduate extends Student{
public void testing() {//和 Pupil 不一样
System.out.println("大学生 " + name + " 正在考大学数学..");
}
}
package com.hspedu.extend_;
public class Extends01 {
public static void main(String[] args) {
Graduate graduate = new Graduate();
graduate.name = "金角大王";
graduate.age = 21;
graduate.testing();
graduate.setScore(87);
graduate.showInfo();
System.out.println("===============");
Pupil pupil = new Pupil();
pupil.name = "银角大王";
pupil.age = 11;
pupil.testing();
pupil.setScore(90);
pupil.showInfo();
}
}
控制台输出结果
子类继承了父类所有的属性和方法,非私有的属性和方法可以在子类直接访问,私有属性不能在子类直接访问,要通过父类的public方法去访问
父类
package com.hspedu.extend_;
public class Base {//父类
//四个属性,四个访问修饰符
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
//默认的无参构造器
public Base() {
System.out.println("Base()....");
}
//四个方法,访问修饰符对应四个属性
public void test100(){
System.out.println("test100");
}
protected void test200(){
System.out.println("test200");
}
void test300(){
System.out.println("test300");
}
private void test400(){
System.out.println("test400");
}
}
子类
package com.hspedu.extend_;
public class Sub extends Base{//子类
public Sub() {
System.out.println("Sub()....");
}
public void sayOk(){
//除了private属性和方法之外都可以访问
}
}
子类中不能访问父类中的private属性和方法
要通过父类提供的public方法去访问
package com.hspedu.extend_;
public class Base {//父类
//四个属性,四个访问修饰符
public int n1 = 100;
public final int n5 = 500;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
//默认的无参构造器
public Base() {
System.out.println("Base()....");
}
//父类提供一个public方法,子类可以访问父类的private属性
public int getN4(){
return n4;
}
//四个方法,访问修饰符对应四个属性
public void test100(){
System.out.println("test100");
}
protected void test200(){
System.out.println("test200");
}
void test300(){
System.out.println("test300");
}
private void test400(){
System.out.println("test400");
}
//父类提供一个public方法,子类可以访问父类的private方法
public void callTest400(){
test400();
}
}
package com.hspedu.extend_;
public class Sub extends Base{//子类
public Sub() {
System.out.println("Sub()....");
}
public void sayOk(){
//除了private属性和方法之外都可以访问
System.out.println(n1 + " " + n2 + " " + n3);
test100();
test200();
test300();
//test400();
System.out.println("n4= " + getN4());
callTest400();//中转
}
}
package com.hspedu.extend_;
public class ExtendsDetail {
public static void main(String[] args) {
//细节说明
Sub sub = new Sub();
sub.sayOk();
}
}
子类必须调用父类的构造器, 完成父类的初始化
当创建子类对象时, 不管使用子类的哪个构造器, 默认情况下总会去调用父类的无参构造器, 如果父类没有提供无参构造器, 则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作, 否则, 编译不会通过
package com.hspedu.extend_;
public class Base {//父类
//四个属性,四个访问修饰符
public int n1 = 100;
public final int n5 = 500;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
//默认的无参构造器
// public Base() {
// System.out.println("父类Base()的构造器被调用....");
// }
//父类的有参构造器
public Base(String name, int age){
System.out.println("父类的(String name, int age)构造器被调用....");
}
//父类提供一个public方法,子类可以访问父类的private属性
public int getN4(){
return n4;
}
//四个方法,访问修饰符对应四个属性
public void test100(){
System.out.println("test100");
}
protected void test200(){
System.out.println("test200");
}
void test300(){
System.out.println("test300");
}
private void test400(){
System.out.println("test400");
}
//父类提供一个public方法,子类可以访问父类的private方法
public void callTest400(){
test400();
}
}
package com.hspedu.extend_;
public class Sub extends Base{//子类
public Sub() {
//super();//隐藏的,创建子类对象的时候会默认调用父类的无参构造器
super("smith",20);//当父类的无参构造器被覆盖时,用super指定调用父类的构造器
System.out.println("子类Sub()构造器被调用....");
}
//当创建子类对象时, 不管使用子类的哪个构造器, 默认情况下总会去调用父类的无参构造器
public Sub(String name){
super("smith",20);
System.out.println("子类Sub(String name)构造器被调用....");
}
public void sayOk(){
//除了private属性和方法之外都可以访问
System.out.println(n1 + " " + n2 + " " + n3);
test100();
test200();
test300();
//test400();
System.out.println("n4= " + getN4());
callTest400();//中转
}
}
package com.hspedu.extend_;
public class ExtendsDetail {
public static void main(String[] args) {
//细节说明
Sub sub = new Sub();
System.out.println("=========第二个对象==========");
Sub sub2 = new Sub("jack");
//sub.sayOk();
}
}
如果希望指定去调用父类的某个构造器, 则显式的调用一下 : super(参数列表)
package com.hspedu.extend_;
public class Base {//父类
//四个属性,四个访问修饰符
public int n1 = 100;
public final int n5 = 500;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
//默认的无参构造器
public Base() {
System.out.println("父类Base()的构造器被调用....");
}
//父类的有参构造器,两个参数
public Base(String name, int age){
System.out.println("父类的(String name, int age)构造器被调用....");
}
//一个参数的构造器
public Base(String name){
System.out.println("父类的(String name)构造器被调用....");
}
//父类提供一个public方法,子类可以访问父类的private属性
public int getN4(){
return n4;
}
//四个方法,访问修饰符对应四个属性
public void test100(){
System.out.println("test100");
}
protected void test200(){
System.out.println("test200");
}
void test300(){
System.out.println("test300");
}
private void test400(){
System.out.println("test400");
}
//父类提供一个public方法,子类可以访问父类的private方法
public void callTest400(){
test400();
}
}
package com.hspedu.extend_;
public class Sub extends Base{//子类
public Sub(String name, int age){
//1.调用父类的无参构造器,两种方式:一是super(); 二是什么都不写
//super();
//2.调用父类Base(String name)的构造器
//super("smith");
//3.调用父类Base(String name, int age)的构造器
super("smith",25);
System.out.println("子类的(String name, int age)构造器被调用....");
}
public Sub() {
//super();//隐藏的,创建子类对象的时候会默认调用父类的无参构造器
super("smith",20);//当父类的无参构造器被覆盖时,用super指定调用父类的构造器
System.out.println("子类Sub()构造器被调用....");
}
//当创建子类对象时, 不管使用子类的哪个构造器, 默认情况下总会去调用父类的无参构造器
public Sub(String name){
super("smith",20);
System.out.println("子类Sub(String name)构造器被调用....");
}
public void sayOk(){
//除了private属性和方法之外都可以访问
System.out.println(n1 + " " + n2 + " " + n3);
test100();
test200();
test300();
//test400();
System.out.println("n4= " + getN4());
callTest400();//中转
}
}
package com.hspedu.extend_;
public class ExtendsDetail {
public static void main(String[] args) {
//细节说明
// Sub sub = new Sub();
// //sub.sayOk();
// System.out.println("=========第二个对象==========");
// Sub sub2 = new Sub("jack");
System.out.println("=========第三个对象==========");
Sub sub3 = new Sub("ada", 46);
}
}
super 在使用时, 必须放在构造器第一行(super 只能在构造器中使用)
super() 和 this() 都只能放在构造器第一行, 因此这两个方法不能共存在一个构造器
java 所有类都是 Object 类的子类, Object 是所有类的基类
TopBase类
public class TopBase {//父类是Object类
public TopBase() {
System.out.println("构造器TopBase()被调用...");
}
}
Base类
package com.hspedu.extend_;
public class Base extends TopBase{//父类
//四个属性,四个访问修饰符
public int n1 = 100;
public final int n5 = 500;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
//默认的无参构造器
public Base() {
System.out.println("父类Base()的构造器被调用....");
}
//父类的有参构造器,两个参数
public Base(String name, int age){
System.out.println("父类的(String name, int age)构造器被调用....");
}
//一个参数的构造器
public Base(String name){
System.out.println("父类的(String name)构造器被调用....");
}
//父类提供一个public方法,子类可以访问父类的private属性
public int getN4(){
return n4;
}
//四个方法,访问修饰符对应四个属性
public void test100(){
System.out.println("test100");
}
protected void test200(){
System.out.println("test200");
}
void test300(){
System.out.println("test300");
}
private void test400(){
System.out.println("test400");
}
//父类提供一个public方法,子类可以访问父类的private方法
public void callTest400(){
test400();
}
}
Sub类
package com.hspedu.extend_;
//ctrl+H查看继承关系
public class Sub extends Base{//子类
public Sub(String name, int age){
//1.调用父类的无参构造器,两种方式:一是super(); 二是什么都不写
//super();
//2.调用父类Base(String name)的构造器
//super("smith");
//3.调用父类Base(String name, int age)的构造器
super("smith",25);
//super() 和 this() 都只能放在构造器第一行, 因此这两个方法不能共存在一个构造器
//this("jack");//调用本类的Sub(String name)构造器
System.out.println("子类的(String name, int age)构造器被调用....");
}
public Sub() {
//super();//隐藏的,创建子类对象的时候会默认调用父类的无参构造器
super("smith",20);//当父类的无参构造器被覆盖时,用super指定调用父类的构造器
System.out.println("子类Sub()构造器被调用....");
}
//当创建子类对象时, 不管使用子类的哪个构造器, 默认情况下总会去调用父类的无参构造器
public Sub(String name){
super("smith",20);
System.out.println("子类Sub(String name)构造器被调用....");
}
public void sayOk(){
//除了private属性和方法之外都可以访问
System.out.println(n1 + " " + n2 + " " + n3);
test100();
test200();
test300();
//test400();
System.out.println("n4= " + getN4());
callTest400();//中转
}
}
package com.hspedu.extend_;
public class ExtendsDetail {
public static void main(String[] args) {
//细节说明
// Sub sub = new Sub();
// //sub.sayOk();
// System.out.println("=========第二个对象==========");
// Sub sub2 = new Sub("jack");
System.out.println("=========第三个对象==========");
Sub sub3 = new Sub("ada", 46);
}
}
父类构造器的调用不限于直接父类! 将一直往上追溯直到 Object 类(顶级父类)
子类最多只能继承一个父类(指直接继承), 即 java 中是单继承机制
不能滥用继承, 子类和父类之间必须满足 is-a 的逻辑关系
一个继承的代码案例
package com.hspedu.extend_;
/**
* 讲解继承的本质
*/
public class ExtendsTheory {
public static void main(String[] args) {
Son son = new Son();
}
}
class GrandPa{//爷爷类
String name = "大头爷爷";
String hobby = "旅游";
}
class Father extends GrandPa {//父类
String name = "大头爸爸";
int age = 39;
}
class Son extends Father { //子类
String name = "大头儿子";
}
1)类加载的顺序
以上代码中创建子类对象,首先在方法区内进行类的加载,加载顺序是Object----->GrandPat----->Fathert----->Son
2)在堆中给对象分配一个地址空间,空间内的属性如下图所示
3)访问时的查找顺序
package com.hspedu.extend_;
/**
* 讲解继承的本质
*/
public class ExtendsTheory {
public static void main(String[] args) {
Son son = new Son();
//按照查找关系来返回信息
//(1) 首先看子类是否有该属性
//(2) 如果子类有这个属性,并且可以访问,则返回信息
//(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)
//(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object...
System.out.println(son.name);//大头儿子
//System.out.println(son.age);//39,不能访问私有属性,但是空间中这个属性依然是存在的
//通过父类提供的公共方法来访问private属性
System.out.println(son.getAge());
System.out.println(son.hobby);//旅游
}
}
class GrandPa{//爷爷类
String name = "大头爷爷";
String hobby = "旅游";
//Father中的age是private修饰的,不能直接访问,
// 就算GrandPa中有属性age,也不会跳过Father去访问GrandPa
int age = 68;
}
class Father extends GrandPa {//父类
String name = "大头爸爸";
private int age = 39;
public int getAge() {
return age;
}
}
class Son extends Father { //子类
String name = "大头儿子";
}
Computer类-父类
package com.hspedu.extend_;
public class Computer {
private String cpu;
private int memo;
private int disk;
public Computer(String cpu, int memo, int disk) {
this.cpu = cpu;
this.memo = memo;
this.disk = disk;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public int getMemo() {
return memo;
}
public void setMemo(int memo) {
this.memo = memo;
}
public int getDisk() {
return disk;
}
public void setDisk(int disk) {
this.disk = disk;
}
public String getDetails(){
return "CPU:" + cpu + " 内存:" + memo + " 硬盘:" + disk;
}
}
package com.hspedu.extend_;
public class PC extends Computer{
private String brand;
public PC(String cpu, int memo, int disk, String brand) {
super(cpu, memo, disk);
this.brand = brand;
}
public void printInfo() {
System.out.print( "PC信息=");
System.out.println(getDetails() + " 品牌:" + brand);
}
}
package com.hspedu.extend_;
public class NotePad extends Computer{
private String color;
public NotePad(String cpu, int memo, int disk, String color) {
super(cpu, memo, disk);
this.color = color;
}
public void printInfo() {
System.out.print( "NotePad信息=");
System.out.println(getDetails() + " 颜色:" + color);
}
}
package com.hspedu.extend_;
public class ExtendsExercise03 {
public static void main(String[] args) {
PC pc = new PC("AMD", 12, 512, "联想");
NotePad notePad = new NotePad("Inter", 12, 420, "银灰");
pc.printInfo();
notePad.printInfo();
}
}