1、继承
继承概念的引入:
/**
* 学生类
*/
public class Student{
//成员变量
String name;
int age;
//get和set方法
//构造方法
public Student(){
}
//成员方法
public void eat(){}
public void sleep(){}
}
/**
* 教师类
*/
public class Teacher{
//成员变量
String name;
int age;
//get,set方法
//构造方法
public Teacher(){
}
//成员方法
public void eat(){}
public void sleep(){}
}
分析: 通过代码我们发现这两个类中有许多相同的代码,如果继承定义工人类,军人类等等,重复的代码会越来越多。
*思考: *我们能否将相同的代码提取出来,定义到一个独立的类中,并让这多个类与这个独立的类产生一个关系,有了这个关系,就可以让这多个类具有这个独立类的属性和功能,为了实现这个功能,Java提供了一个技术,叫做:继承。
继承的格式: 关系需要借助一个关键字—extends
class Fu{
}
class Zi extends Fu{
}
改进案例:
class Person {//人类
String name;
int age;
//get和set方法
public void eat(){}
public void sleep(){}
}
/**
* 学生类
*/
class Student extends Person{
public Student(){}
}
/**
* 教师类
*/
class Teacher extends Person{
public Teacher(){}
}
代码演示:
package org.wdzl.unit03;
/**
* 继承的演示案例:
* 继承:把多个类中相同代码提取到独立的类中。
* 实现继承:使用extends关键字
* 格式:子类 extends 父类{}
* 优点:1、提高了代码的复用性
* 2、提高了代码的维护性
* 3、继承是多态的前提
*/
public class ExtendsDemo1 {
public static void main(String[] args) {
//使用继承前
Student1 student1 = new Student1();
student1.eat();
student1.sleep();
System.out.println("------------");
Teacher1 teacher1 = new Teacher1();
teacher1.eat();
teacher1.sleep();
}
}
class Person{
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
class Student1 extends Person{
}
class Teacher1 extends Person{
}
继承的概述: 多个类中存在相同的属性和行为时,将这些内容抽取到单独的一个类中,那么多个类无需在定义这些属性和行为,只需要继承那个类即可。
package org.wdzl.unit03;
/**
* 继承的演示案例:
* 继承:把多个类中相同代码提取到独立的类中。
* 实现继承:使用extends关键字
* 格式:子类 extends 父类{}
* 优点:1、提高了代码的复用性
* 2、提高了代码的维护性
* 3、继承是多态的前提
*/
public class ExtendsDemo1 {
public static void main(String[] args) {
//使用继承前
Student1 student1 = new Student1();
student1.eat();
student1.sleep();
System.out.println("------------");
Teacher1 teacher1 = new Teacher1();
teacher1.eat();
teacher1.sleep();
}
}
class Person{
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
class Student1 extends Person{
}
class Teacher1 extends Person{
}
继承的优点:
1、提高了代码的复用性
2、方便了代码的维护
3、继承的多态的前提
继承的缺点: 1、增加了类与类之间的耦合性。违背了面向对象的初衷:“高内聚,低耦合”
内聚:就是自己完成某事的能力。
耦合:类与类之间的关系
2、打破了封装性。
package org.wdzl.unit03;
/**
* 继承的演示案例:
* 继承:把多个类中相同代码提取到独立的类中。
* 实现继承:使用extends关键字
* 格式:子类 extends 父类{}
* 优点:1、提高了代码的复用性
* 2、提高了代码的维护性
* 3、继承是多态的前提
*/
public class ExtendsDemo1 {
public static void main(String[] args) {
//使用继承前
Student1 student1 = new Student1();
student1.eat();
student1.sleep();
System.out.println("------------");
Teacher1 teacher1 = new Teacher1();
teacher1.eat();
teacher1.sleep();
}
}
class Person{
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
class Student1 extends Person{
}
class Teacher1 extends Person{
}
继承的特点: 1、单继承:只能有一个父类
2、多层继承,实现继承体系。
单独的这个类称为父类,基类或者超类;这多个类叫做子类或者派生类。
有了这个继承以后,我们定义一个类的时候可以在一个已经存在的类的基础上,还可以定义自己新的成员。
package org.wdzl.unit03;
/**
* 继承的特点:
* 1、Java中,只支持单继承
* 2、Java支持多层继承,可以形成一个继承体系。
*/
public class ExtendsDemo2 {
public static void main(String[] args) {
Son son = new Son();
son.method2();
son.method1();
}
}
class Father extends GrandFather{
public void method1(){
System.out.println("我是你爹!!!");
}
}
class GrandFather{
public void method2(){
System.out.println("我是你爷爷!!!");
}
}
class Son extends Father{
}
继承的注意事项:
1、子类不能继承的私有方法,但是可以继承父类的私有属性
2、子类不能继承父类的构造方法,但是可以通过super关键字来访问,
3、如果类与类之间的关系符合“is a”关系,我们就可以选择使用继承,不要因为一个类的某几个功能而去继承。
package org.wdzl.unit03;
/**
* 继承在使用时的注意事项:
* 1、子类只能继承父类非私有的成员变量和方法。
* 2、子类不能继承父类的构造方法,但是可以通过super(后面讲)关键字去访问。
* 3、在选择继承时,不要因为某一部分来选择继承,我们在选择继承时,要注意一种关系:“is a”
* 举例:
* class A {
* function();
* function2();
* }
* class B {
* function2();
* function3();
* }
* 我们发现代码中,class B和class A都有function2(),我们就使用继承修饰class B
* class B extends A{
* function2();
* }
* 这样改造存在一个问题:就是class B中无形多出可一个function()方法,然而这个方法不是我们想要的。
*
*/
public class ExtendsDemo3 {
public static void main(String[] args) {
//创建对象
Son2 son2= new Son2();
son2.method();
//子类不能访问父类的私有方法
// son2.function();//爆红
}
}
class Father2{
//成员变量
public int num = 2;
private int num2 = 4;
//构造方法
public Father2(){
System.out.println("无参构造");
}
//成员方法
public void method(){
System.out.println(num);
System.out.println(num2);
}
private void function(){
System.out.println(num);
System.out.println(num2);
}
}
class Son2 extends Father2{
public Son2(){
super();
}
public void method2(){
System.out.println(num);
//子类不能访问父类的私有成员变量
// System.out.println(num2);//爆红
}
}
继承中成员的关系:
1、成员变量:
a、变量相同:
①:在子类的局部位置查找
②:在子类的成员位置寻找
③:在父类的成员位置寻找
④:还找不到就报错
b、变量名不同
package org.wdzl.unit03;
/**
* 父类的成员变量和子类的成员变量同名问题
* 1、在子类的局部变量中寻找;
* 2、在子类的成员变量中寻找;
* 3、在父类的成员变量中寻找;
* 4、在以上三个地方都没有找到就会报错。
* 注意:继承不能继承父类的局部变量。
*/
public class ExtendsDemo4 {
public static void main(String[] args) {
Son3 son3 = new Son3();
System.out.println(son3.function());
}
}
class Father3{
public int a = 10;
public int method(){
int a = 20;
return a;
}
}
class Son3 extends Father3{
//public int a = 30;
public int function(){
//int a = 40;
return a;
}
}
2、构造方法
1、子类的所有构造方法会默认的去访问父类的无参构造。
原因:子类如果要访问父类的属性或方法,则父类也需要完成初始化。
package org.wdzl.unit03;
/**
* 继承中的构造方法的关系
* 1、子类中所有的构造方法都默认会访问父类中的无参构造方法
* 原理:
* 因为继承的关系,子类对象可以使用父类的非私有的属性和方法,所以在创建子类对象前必须对父类的数据进行初始化,也急就是创建父类的对象。
*
* 注意:
* 子类的所有构造方法第一句都是super();
*/
public class ExtendsDemo6 {
public static void main(String[] args) {
Son5 son5 = new Son5();
System.out.println("------------");
Son5 son51 = new Son5("张三");
}
}
class Father5{
public Father5(){
System.out.println("父类的无参构造方法");
}
public Father5(String name){
System.out.println("父类的有参构造方法");
}
}
class Son5 extends Father5{
public Son5(){
System.out.println("子类中的无参构造方法");
}
public Son5(String name){
System.out.println("子类的有参构造");
}
}
2、父类中没有无参构造解决办法:
a、通过super关键字指定的访问父类的有参构造方法,完成父类的初始化。
b、通过this关键字调用自身其他的构造方法,但是调用的这个构造方法必须完成对父类构造方法的访问。
c、手动的给出父类无参构造
package org.wdzl.unit03;
/**
* 继承中构造方法的注意事项:
* 1、如果父类中没有无参构造方法,我们将无法创建子类对象。
* 解决办法:
* a、在父类中添加无参构造
* b、通过使用super关键字指定调用父类的有参构造方法。
* c、子类可以通过this去调用本类中的其他构造方法,
* 子类一定要有一个构造方法访问了父类地构造方法,这样才能保证父类能够被初始化。
* 2、this和super关键字都必须放在构造方法地第一句,并且不能同时使用这两个关键字。
*
*/
public class ExtendsDemo7 {
public static void main(String[] args) {
//Son6 son6 = new Son6();
Son6 son61 = new Son6("张三");
}
}
class Father6{
// public Father6(){
// System.out.println("父类的无参构造");
// }
public Father6(String name){
System.out.println("父类的有参构造方法");
}
}
class Son6 extends Father6{
public Son6(){
super("张三丰");//显性的指定调用父类的有参构造
System.out.println("子类的无参构造方法");
}
public Son6(String name){
this();//本类的无参构造
System.out.println("子类的有参构造");
}
}
3、成员方法
a、方法名相同(方法的重写override)
①:先去子类寻找
②:在父类中寻找
③:找不到就报错
b、方法名不同
package org.wdzl.unit03;
/**
* 继承关系中成员方法地关系:
* 1、方法名不同
* 2、方法名相同:
* a、首先在子类中找
* b、在父类中找
* c、都找不到就报错
* 方法地重写:对子类和父类中方法声明完全一致,就称为方法重写。
* 方法重载:一个类中方法名相同,参数列表不用。
* 方法重写的应用:可以重写父类的方法,使子类即拥有父类同名的方法,也可以拥有子类自己特有的方法。
*/
public class ExtendsDemo8 {
public static void main(String[] args) {
NewPhone newPhone = new NewPhone();
newPhone.playGame("英雄联盟");
newPhone.watchNews("新闻");
System.out.println("----------------");
newPhone.call("拜登");
}
}
class Phone{
public void call(String name){
System.out.println("给"+name+"打电话");
}
public void watchNews(String news){
System.out.println("看新闻");
}
}
class NewPhone extends Phone{
public void call(String name){
super.call("唐某人");
System.out.println("给"+name+"打视频电话");
}
public void playGame(String name){
System.out.println("玩"+name+"这个游戏");
}
}
面试题:
1、override和overload的区别?overload是否可以改变返回值类型?
override 重写
首先实在两个类中,并且这两个类是继承关系,然后在子类中,方法的声明与父类的声明基本一样(访问修饰符可以不同,子类不能比父类小)
overload 重载
首先是在一个类中,然后,出现多个方法名相同,但是参数列表不同的情况下,这种情况叫做重载,方法重载只与参数列表有关,与返回值没有关系。
package org.wdzl.unit03;
/**
* 方法重写注意事项:
* 1、父类的私有方法不能被重写。
* 2、子类重写父类的方法时,访问权限不能降低。
* 3、父类的静态方法,子类也必须通过静态方法去重写。(它不是方法重写,但表现形式和方法重写类似)
* 子类覆盖父类的方法时,只要保证声明完全一致,就不会出现上面的一些问题。
*/
public class ExtendsDemo9 {
public static void main(String[] args) {
Son7 son7 = new Son7();
Son7.function();
}
}
class Father7{
// private void method(){
// System.out.println("Father method");
// }
void method(){
System.out.println("Father method");
}
public static void function(){
System.out.println("Father static function");
}
}
class Son7 extends Father7{
// private void method(){
// System.out.println("Son method");
// }
public void method(){
System.out.println("Father method");
}
public static void function(){
System.out.println("Son static function");
}
}
2、this和super关键字的区别,以及各自的应用场景?
this:代表当前类的对象的引用
super:代表对父类对象的引用
应用场景:
成员变量:
this.成员变量:当前类的成员变量
super.成员变量:父类的成员变量
成员方法:
this.成员方法:当前类的成员方法
super.成员方法:访问父类的成员方法
构造方法:
package org.wdzl.unit03;
/**
* 如果我们想输出子类的成员变量的值,该如何操作?
* 使用this关键字
* 如果我们想输出父类的成员变量的值,该如何操作?
* 可以借助super关键字
* this和super之间的区别:
* 概念上的区别:
* 1、this代表的是当前类的引用,我们实例起的对象名就是存储的地址。
* 2、super代表的是父类的引用,
* 使用上的区别:
* 1、调用成员变量
* this.成员变量:调用的是本类的成员变量
* super.成员变量:调用的是父类的成员变量
* 2、调用构造方法
* this.构造方法:调用本类的构造方法
* super.构造方法:调用父类的构造方法
* 3、调用成员方法
* this.成员方法:调用本类的构造方法
* super.成员方法:调用父类的构造方法
*
*/
public class ExtendsDemo5 {
public static void main(String[] args) {
Son4 son4 = new Son4();
System.out.println(son4.function());
}
}
class Father4{
public int a = 10;
public int method(){
int a = 20;
return a;
}
}
class Son4 extends Father4{
public int a = 30;
public int function(){
int a = 40;
//return this.a;
return super.a;
}
}
this.(……):当前类的构造方法
this.(……):父类的构造方法
package org.wdzl.unit03;
/**
* 继承中构造方法的注意事项:
* 1、如果父类中没有无参构造方法,我们将无法创建子类对象。
* 解决办法:
* a、在父类中添加无参构造
* b、通过使用super关键字指定调用父类的有参构造方法。
* c、子类可以通过this去调用本类中的其他构造方法,
* 子类一定要有一个构造方法访问了父类地构造方法,这样才能保证父类能够被初始化。
* 2、this和super关键字都必须放在构造方法地第一句,并且不能同时使用这两个关键字。
*
*/
public class ExtendsDemo7 {
public static void main(String[] args) {
//Son6 son6 = new Son6();
Son6 son61 = new Son6("张三");
}
}
class Father6{
// public Father6(){
// System.out.println("父类的无参构造");
// }
public Father6(String name){
System.out.println("父类的有参构造方法");
}
}
class Son6 extends Father6{
public Son6(){
super("张三丰");//显性的指定调用父类的有参构造
System.out.println("子类的无参构造方法");
}
public Son6(String name){
this();//本类的无参构造
System.out.println("子类的有参构造");
}
}