1.继承的简介
继承是面向对象编程中的一个重要概念,指的是一个类可以继承另一个类的属性和方法。被继承的类称为父类或基类,继承这个父类的类称为子类或派生类。
通过继承,子类可以继承父类的属性和方法,使得子类具有相似的行为和特性。子类可以重写父类的方法,覆盖父类的行为,也可以添加新的属性和方法以扩展父类的功能。
继承提供了代码复用的机制,可以减少代码的冗余,并且使得代码更加可维护和扩展。此外,继承也是实现多态的基础,通过父类的引用可以调用子类的方法,实现不同对象的统一操作。
2.代码中的体现
定义一个名为“Person”的类,作为父类
public class Person{String name;int age;public Person(){}public Person(String name ,int age){this.name = name;this.age = age;}public void eat(){System.out.println("人会吃~");}public void sleep(){System.out.println("人会睡~");}}
这段代码定义了一个名为Person的类。Person类包含了两个属性:name(姓名)和age(年龄)。类中定义了两个构造方法:一个无参构造方法和一个带有参数的构造方法,用于初始化name和age属性。
类中还定义了两个方法:eat(吃)和sleep(睡)。这两个方法是公共的(public),可以被其他类调用。eat方法输出了一个字符串,表示人会吃;sleep方法输出了一个字符串,表示人会睡。
这个Person类是一个基类,其他类可以通过继承这个类来继承其中的属性和方法。通过继承,子类可以使用Person类中定义的属性和方法,并可以根据需要进行扩展和修改。
定义一个名为“Teacher”的子类用来继承“Person”
public class Teacher extends Person{int teachAge;//构造方法public Teacher(){}public Teacher(int teachAge) {TeachAge = teachAge;}public Teacher(String name,int age,int teachAge){super(name, age);TeachAge = teachAge;}public void teach(){System.out.println("老师会教书");}
}
这段代码定义了一个名为Teacher的类,它继承自Person类。Teacher类有一个额外的实例变量teachAge,表示教学年龄。
代码中提供了三个构造方法:
- 无参构造方法:没有参数,什么也不做。
- 带参构造方法:接受一个int类型的teachAge参数,并将其赋值给Teacher类的实例变量teachAge。
- 带参构造方法:接受一个字符串name、一个int类型的age和一个int类型的teachAge参数,并将name和age传递给父类Person的构造方法进行初始化,同时将teachAge赋值给Teacher类的实例变量teachAge。
Teacher类还有一个公共方法teach(),它打印出"老师会教书"的消息。
这段代码实现了一个教师类,继承了人类,具有教学年龄的特征,并能够进行教学操作。
最后定义一个功能测试类
public class Test {public static void main(String[] args) {Person person = new Person("hh",22);System.out.println(person.name+","+person.age);person.eat();person.sleep();Teacher teacher = new Teacher("jj",56,12);System.out.println(teacher.name+","+teacher.age+","+teacher.TeachAge);teacher.eat();teacher.sleep();teacher.teach();}
}
输出结果:
这段代码定义了一个名为Test的类,其中有一个main方法作为程序的入口点。
在main方法中,首先创建了一个Person对象,通过调用Person类的带参构造方法,传入字符串"hh"和整数22进行初始化。然后通过person对象访问其实例变量name和age,并将结果打印出来。接着调用person对象的eat()方法和sleep()方法,输出对应的消息。
接下来,创建了一个Teacher对象,通过调用Teacher类的带参构造方法,传入字符串"jj"、整数56和整数12进行初始化。然后通过teacher对象访问其父类Person中的name和age实例变量,以及Teacher类自身的TeachAge实例变量,并将结果打印出来。接着调用teacher对象的eat()方法和sleep()方法,输出对应的消息。最后通过调用teacher对象的teach()方法,输出"老师会教书"的消息。
3.super关键字的用法
1.定义一个名为“Father”的父类
public class Father {String name;int age;public Father() {}public Father(String name, int age) {this.name = name;this.age = age;}public void eat(){System.out.println(this.age+"岁的"+this.name+"会吃");}
}
这段代码定义了一个名为Father的类。Father类有两个实例变量name和age,代表父类成员中的姓名和年龄。
Father类有两个构造方法,一个是无参构造方法,一个是有参构造方法。无参构造方法不进行任何操作,而有参构造方法用于初始化父类成员的姓名和年龄。
Father类还有一个eat()方法,用于输出年龄和姓名,并打印一句话表示吃的动作。
2.定义一个名为“Child”的子类用来继承“Father”父类
public class Child extends Father{String name;int age;public Child() {}public Child(String name, int age) {this.name = name;this.age = age;}public Child(String name, int age, String name1, int age1) {super(name, age);this.name = name1;this.age = age1;}public void eat(){//所表达的是子类中的“xx岁的xx会吃”System.out.println(this.age+"岁的"+this.name+"会吃");//表达父类中的”xx岁的xx会吃“System.out.println(super.age+"岁的"+super.name+"会吃");//还可以用super关键字直接调用父类中的方法//super.eat();}
}
这段代码定义了一个名为Child的类,它继承自Father类。Child类也有两个实例变量name和age,但是它们与父类的name和age变量没有任何关系。
Child类有三个构造方法,一个是无参构造方法,一个是有两个参数的构造方法,另一个是有四个参数的构造方法。其中有两个参数的构造方法用于初始化子类自己的name和age变量,四个参数的构造方法不仅会调用父类的有参构造方法来初始化父类的name和age变量,还会初始化子类的name和age变量。
Child类也有一个eat()方法,但是与父类的eat()方法不同的是,它重写了父类的eat()方法。在重写的eat()方法中,首先输出子类自己的年龄和姓名,并打印一句话表示吃的动作。然后使用super关键字调用父类的age和name变量,并打印一句话表示父类的吃的动作。最后还可以使用super关键字直接调用父类的eat()方法。
3.定义一个测试类
public class Test {public static void main(String[] args) {Father father = new Father("老贺",40);father.eat();Child child = new Child("小贺",18);child.eat();}
}
注意:输出结果的后一行有点反常,这是因为只调用了Child类中的public Child(String name, int age)方法,而这是属于子类中的方法,和父类没有关系
public void eat(){//所表达的是子类中的“xx岁的xx会吃”System.out.println(this.age+"岁的"+this.name+"会吃");//表达父类中的”xx岁的xx会吃“System.out.println(super.age+"岁的"+super.name+"会吃");//还可以用super关键字直接调用父类中的方法//super.eat();}
所以eat()方法中后一个语句输出的是父类中所定义的值,要想正常输出,则需要重新new一个child
对象,用来调用public Child(String name, int age, String name1, int age1) 这个构造方法
更改后的主函数:
public class Test {public static void main(String[] args) {Father father = new Father("老贺",40);father.eat();Child child = new Child("小贺",18,"老贺",40);child.eat();}
}
输出结果
4.继承中的权限
在Java中,继承中的权限是通过访问修饰符来控制的。Java中有四种访问修饰符:public、protected、default和private。
-
public:被public修饰的成员(字段、方法、构造方法等)可以在任何地方被访问,包括其他类、不同包中的类和子类。
-
protected:被protected修饰的成员可以被同一个包中的类访问,也可以被不同包中的子类访问,但不能被不同包中的非子类访问。
-
default:没有修饰符的成员(也称为包级访问)可以被同一个包中的类访问,但不能被不同包中的类访问。
-
private:被private修饰的成员只能在同一个类中访问,不能被其他类或子类访问。
继承关系中,子类会继承父类的字段和方法。下面是不同访问修饰符下,子类对父类成员的访问权限:
-
public成员:子类可以访问父类的public成员。
-
protected成员:子类可以访问父类的protected成员,即使子类和父类不在同一个包中。
-
default成员:子类可以访问父类的default成员,但是要求子类和父类在同一个包中。
-
private成员:子类无法直接访问父类的private成员,只能通过父类的public/protected/default方法来间接访问。
需要注意的是,子类可以覆盖(重写)父类的方法,但是子类不能降低父类方法的访问权限。也就是说,如果父类的方法是public,那么子类重写该方法时也必须使用public修饰符。同样地,如果父类的方法是protected,子类重写该方法时可以使用protected或public修饰符,但不能使用private修饰符或不使用任何修饰符(default)。
这样设计的目的是为了保护类的封装性和安全性,子类只能通过继承和重写的方式来访问和修改父类的成员。
public class Fu {public String varPub = "我是公成员变量";private String varPri = "我是私有成员变量";protected String varPro = "我是受保护的变量";String varDef = "我是默认的变量";
}
public class Zi extends Fu{public void method(){System.out.println(this.varPub);//System.out.println(this.varPri);System.out.println(this.varPro);System.out.println(this.varDef);}
}
public class TS {public static void main(String[] args) {Fu fu = new Fu();System.out.println(fu.varPub);System.out.println(fu.varPro);System.out.println(fu.varDef);//System.out.println(fu.varPri);//除了private,其他权限都可在当前包内的其他类中都可访问System.out.println("============");Zi zi = new Zi();zi.method();//同包子类中,除私有权限外都可调用}
}
在另一包内在定义一个子类
public class Zi extends Fu {public void method(){System.out.println(super.varPub);//System.out.println(super.varPri);System.out.println(super.varPro);//System.out.println(super.varDef);//不同包子类,只有公有和受保护变量可以被调用}
}
public class Ts {public static void main(String[] args) {Zi zi = new Zi();zi.method();}
}