1.继承
为什么要继承
package Inherit; class Dog{ public String name; public int age; public void barks(){ System.out.println("汪汪叫"); } public void eat(){ System.out.println("正在吃饭咪咪"); } } class Cat{ public String name; public int age; public void eat(){ System.out.println("正在吃饭咪咪"); } public void catchMouse(){ System.out.println("正在抓老鼠"); } }
猫和狗有共同的name,age,还有eat方法
那么就可以写一个共同的方法,同时让他们去继承
class Anmials{
public String name;
public int age;
public void eat(){
System.out.println("正在吃饭");
}
}
class Dog extends Anmials{
public void barks(){
System.out.println("汪汪叫");
}
}
class Cat extends Anmials{
public void catchMouse(){
System.out.println("正在抓老鼠");
}
}
语法class Dog子类 extand 继承 Animals父类
当子类继承父类之后,就会把属性和方法全部继承。
继承:就是对共性的抽取,从而达到对代码的复用
package Inherit; class Anmials{ public String name; public int age; public void eat(){ System.out.println("正在吃饭"); } } class Dog extends Anmials{ public void barks(){ System.out.println("汪汪叫"); } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + '}'; } } class Cat extends Anmials{ public void catchMouse(){ System.out.println("正在抓老鼠"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.age = 1; dog.name = "xiaozhang"; System.out.println(dog); dog.barks(); } }
注意
1.子类会将父类中的成员变量或者成员方法继承到子类中
2.子类继承父类之后,必须要新添加自己特有的成员,体现出与其父类的不同,否咋就没有必要继承了
3.一般情况继承不要超过三层
4.私有的成员可以被继承,但是不能访问,能用get,set方法来访问。
2.父类成员的访问
1.子类中访问父类的成员变量
(1)子类和父类不存在同名成员变量
class Base{ int a; int b; } public class Derived { int c; public void method(){ int a = 10;// int b = 20; int c = 30; } }
此时的a和b就是父类中继承下来的a和b
(2)子类和父类存在同名成员变量
优先就近,子类和父类重合,优先访问子类自己的
package Inherit; class Base{ int a; int b; } public class Derived { int c; int a; public void method(){ int a = 10;//当父类和子类都拥有同名的变量的时候,优先访问子类自己 int b = 20; int c = 30; System.out.println(a); System.out.println(b); System.out.println(c); } public static void main(String[] args) { Derived derived = new Derived(); derived.method(); Base base = new Base(); System.out.println(base.a); } } 结果为10 20 30 0
(3)当子类和父类同名的时候,我就要访问父类的,怎么办
class Base{ int a; int b; } public class Derived extends Base{ int c; int a; public void method(){ super.a = 10; b = 20; c = 30; System.out.println(super.a); System.out.println(a); System.out.println(b); System.out.println(c); }
结果:10,0,20,30。
2.子类中访问父类的成员方法
通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到时访问,否则在父类中找,找到时访问,否则编译报错。
通过派生类对象访问父类与子类同名方法时,如果父类的子类同名方法的参数列表不同(重载),根据调用方法传递参数选择合适的方法访问,如果没有则报错(找参数)
问题:当子类和父类有同名的成员方法的时候,如何访问父类的方法
1.成员方法名字不同
package Inherit; class Base1{ int a; int b; public void methodA(){ System.out.println("Base1中的methodA()"); } } public class Derived2 extends Base1{ public void methodB(){ System.out.println("Derived2中的methodB"); } public void methodC(){ methodB(); //访问子类自己的methodB methodA(); //访问父类继承的methodA //methodD(); //编译失败,在整个继承体系中没有发现方法methodD } }
2.成员方法名字相同
package Inherit;
class Base1{
int a;
int b;
public void methodA(){
System.out.println("Base1中的methodA()");
}
public void methodB(){
System.out.println("Base1中的methodB()");
}
}
public class Derived2 extends Base1{
public void methodA(int a){
System.out.println("Derived2中的methodA()");
}
public void methodB(){
System.out.println("Derived2中的methodB方法");
}
public void methodC(){
methodA(10);//访问自己的传参的methodA
methodA(); //访问父类继承的methodA
methodB(); //就近访问子类自己的methodB
}
}
此时Base1中的methodA和Derived1中的methodA就构成了重载
此时Base1中的methodB和Derived1中的methodB就构成了重写
3.super
概念
主要作用是访问父类中的东西
super.data;访问父类中的普通成员变量;
super.function(),访问父类中的普通成员方法;
super();调用父类的构造方法
super只是一个关键字,在代码层面上,能达到容易读的效果。
有些书说,super是父类的引用,这个是错误的说法。
注意
super不能在静态的方法中使用
在子类的成员中访问父类的成员和方法
4.子类构造方法
父子父子,所以:子类对象构造时,需要先调用积累的构造方法,然后再执行子类的构造方法
package Inherit; class Anmials{ public String name; public int age; public void eat(){ System.out.println("正在吃饭"); } public Anmials(String name,int age){ this.age = age; this.name = name; } } class Dog extends Anmials{ public boolean silly; public void barks(){ System.out.println("汪汪叫"); } public Dog(String name,int age,boolean silly){ //1.第一步就是先初始化父类的对象 super(name, age); this.silly = silly; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + '}'; } } public class Test { public static void main(String[] args) { Dog dog = new Dog("xiaozhang",21,false); } }
在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象的时候,先执行基类的构造方法,然后再执行子类的构造方法,因为:子类对象中的成员是有两部分组成的,基类继承下来的以及子类新增加的部分,父子,先有父再有子,所以在构造子类对象的时候,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己的新增加的成员初始化完整
注意
1.若父类是自定义无参数或者是默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用积累构造方法
package Inherit; class Anmials{ public String name; public int age; public void eat(){ System.out.println("正在吃饭"); } public Anmials(){ } } class Dog extends Anmials{ public boolean silly; public void barks(){ System.out.println("汪汪叫"); } public Dog(){ super(); }
2.如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
3.在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中的第一条语句。
4.super(...)只能在子类构造方法中出现一次,并且不能和this一同出现
4.super和this
1.相同点:
都是java中的关键字
只能在类的非静态方法中使用,用来访问非静态 成员方法和字段
在构造方法中调用时,必须是构造方法中的第一条语句,并0且不能同时存在
2.不同点:
this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现
构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写就没有
5.再谈初始化
要是有代码块,它的执行顺序是什么呢?
静态代码块,实例代码块,构造方法
6.protected权限
它可以在不同包的子类中进行访问
package demo1; public class Testdemo { protected int a = 10; public void fun(){ System.out.println(a); } }
package demo2; import demo1.Testdemo; public class Test extends Testdemo { public void test(){ System.out.println(super.a); } public static void main(String[] args) { Test t = new Test(); t.test(); } }
在以后的一般情况,所有属性都是private但不是都是,用哪个访问修饰限定符,需要看你的场景和你的需要
7.final关键字
final可以用作密封
(1)一般来说java的继承最好是在三层内。那么我想要在三层继承后结束继承我可以使用final关键字
例如
class A{ } class B extends A{ } final class C extends B{ } class D extends C{ }
此时这个classD就没法继承,编译器会报错
(2)final int a = 10;他就是常量,即不能修改
(3)final还以修饰方法,密封方法,他不能被重写
8.继承和组合
继承可以看做是is-a的关系
class Dog extends Animals Dog is a Animal
组合:可以看做has-a的关系
class Student{ } class Teacher{ } class school{ public Student[] students; public Teacher[] teachers; }
学校有老师和学生
9.多态
(1)多态的概念
比如:一个人可以有多种状态,开心,生气,郁闷
在程序中理解多态:先明白向上转型,明白重写,明白多态。
具体点就是去完成某个行为不同的对象去完成就会产生处不同的状态
打印机:彩色打印机打的就是彩色的,黑白打印机打的就是黑白的
粮食:猫吃就是猫粮,狗吃就是狗粮
(2)多态实现的条件
必须在继承体系想,子类必须要对父类中的方法进行重写,通过父类的引用来调用重写方法
多态的实现:在代码运行时,当传递不同类对象时,会调用对应类的方法
子类的访问权限要大于等于父类的访问权限(父类是public,子类只能是public)
fault-protected-public,private方法不能重写
class Anmials{ public String name = "Hello"; public int age; public void eat(){ System.out.println("正在吃饭"); } public Anmials(){ } } class Dog extends Anmials{ public boolean silly; public void barks(){ System.out.println("汪汪叫"); } public Dog(){ super(); } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + '}'; } public void eat(){ System.out.println(name + "正在吃狗粮!"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.eat(); }
(3)重写
为了兼容原来的老用户,并且为新用户增加新的使用体验,就可以重写
private修饰的方法,不能重写
final修饰的方法,我们叫做密封方法,不能被重写
如果被static修饰,也不能重写
重写快捷键,鼠标右键Generate,Override methods
(4)重写和重载的区别:
重载:
1.方法名称相同
2.参数列表不同{数据类型,顺序,个数}
3.返回值不做要求
重写:
1.方法名称相同
2.参数列表相同{数据类型,顺序,个数}
3.返回值相同(或者构成父子类关系也可以)
(5)向上转型
向上转型就是把子类的对象给父类
1.静态绑定也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型确实了调用那个方法。典型代表函数重载。
2.动态绑定,也称为后期绑定,即在编译时,不能确定方法的行为,需要等到程序运行时,才能确定具体调用时哪个类的方法。
实现:
class Anmials{ public String name = "Hello"; public int age; public void eat(){ System.out.println("正在吃饭"); } public Anmials(){ } } class Dog extends Anmials { public boolean silly; public void barks() { System.out.println("汪汪叫"); } @Override public void eat() { sout(name +"正在吃狗粮 "); } public class Test { public static void main(String[] args) { Dog dog = new Dog(); Anmials anmials = dog; anmials.eat(); }
(6)向上转型实现
方法1:直接赋值
Anmials anmials = new Dog();
方法2:方法传参过程当中实现向上转型
public static void function2(Anmials anmials){ } public static void main(String[] args) { Dog dog = new Dog(); function2(dog); }
方法3:方法传参
public static Anmials function3(){ return new Dog(); }
(7)多态的实现
package Inherit; class Anmials{ public String name = "Hello"; public int age; public void eat(){ System.out.println("正在吃饭"); } } class Dog extends Anmials { public boolean silly; public void barks() { System.out.println("汪汪叫"); } public void eat() { System.out.println(name + "正在吃狗粮"); } } class Cat extends Anmials{ public void catchMouse(){ System.out.println("正在抓老鼠"); } public void eat(){ System.out.println(name + "正在吃猫粮"); } } public class Test { public static void function2(Anmials anmials){ anmials.eat(); } public static void main(String[] args) { Dog dog = new Dog(); function2(dog); Cat cat = new Cat(); function2(cat); } }
调用了相同的方法,却输出了不同的结果,这个过程的实现就是多态的实现
10.多态的优缺点
第一种呈现:
package demo; class Shape{ public void draw(){ System.out.println("画图形"); } } class Rect extends Shape{ @Override public void draw() { System.out.println("画矩形"); } } class Cycle extends Shape{ @Override public void draw() { System.out.println("画圆形"); } } class Triangle extends Shape{ @Override public void draw() { System.out.println("画一个三角形"); } } public class Test { public static void main(String[] args) { Shape shape = new Rect(); shape.draw(); Shape shape2 = new Cycle(); shape.draw(); Shape shape1 = new Triangle(); shape.draw(); } }
第二种呈现:
package demo; class Shape{ public void draw(){ System.out.println("画图形"); } } class Rect extends Shape{ @Override public void draw() { System.out.println("画矩形"); } } class Cycle extends Shape{ @Override public void draw() { System.out.println("画圆形"); } } class Triangle extends Shape{ @Override public void draw() { System.out.println("画一个三角形"); } } public class Test { public static void DrawMap(Shape shape) { shape.draw(); } public static void main(String[] args) { Rect rect = new Rect(); DrawMap(new Rect()); DrawMap(new Triangle()); DrawMap(new Cycle()); }
在drawMap方法当中,Shape shape引用,引用的子类对象不一样,调用方法表现出来的行为不一样。我们把这种思想就叫做多态。
第三种呈现:
要是没有多态
public static void DrawMap1() { String[] strings = {"cycle","rect","cycle","rect","flower"}; for (String x: strings) { if (x.equals("cycle")){ Cycle cycle = new Cycle(); cycle.draw();; }else if (x.equals("rect")){ Rect rect = new Rect(); rect.draw(); }else { Triangle triangle = new Triangle(); triangle.draw(); } } }
有多态
public static void DrawMap2(){ Shape[] shapes = {new Cycle(),new Rect(),new Cycle(),new Rect(),new Triangle()}; for (Shape s: shapes) { s.draw(); } }
优点:
可以降低代码的圈复杂度,避免使用大量的if-else
可扩展能力更强
缺陷:
代码运行效率降低
属性没有多态性
构造方法没有多态性
11.避免在构造方法中调用重写方法
一段有坑的代码,我们创建两个类,B是父类,D是子类,D中重写func方法,并且在B的构造方法中调用func()
package demo; class B{ public B(){ func(); } public void func(){ System.out.println("B.func()"); } } class D extends B{ private int num = 1; @Override public void func() { System.out.println("D.func()" + num); } } public class Test1 { public static void main(String[] args) { D d = new D(); } }
执行结果为D.func()0
构造D对象的同时,会调用B的构造方法
B的构造方法中调用了func方法,此时会触发动态绑定,会调用到D中的func
此时D对象自身还没有构造,此时的num还在未初始化的状态,值为0,如果具备多态性,num的值应该为1
所以在构造函数内,尽量避免使用实例方法,畜类final和private方法
结论:
用尽量简单的方式使对象进入可工作状态,尽量不要再构造器中调用方法(如果这个方法被子类重写,就会触发动态绑定,但是此时子类对象还没有构造完成,可能会出现一些隐藏的但是又极难发现的问题)
作业一
class C { public String toString() { System.out.println("aaa"); return "bbb"; } } public class Test1 { public static void main(String[] args) { C a = new C(); System.out.println(a); } }
打印出来的是什么?
aaa
bbb
作业二
重载要求两个方法名字相同,但是参数的类型和个数不同,不要求返回值类型相同
重写要求两个方法名字相同,同时参数的类型和个数相同,不要求返回值类型相同
重写的方法可以使用@Override注解来修饰
父类的方法为private的时候,子类方法不能进行重写
作业三
package Homework; class F{ public int Func(){ System.out.println("b"); return 0; } } class D extends F{ @Override public int Func() { System.out.println("d"); return 0; } } public class Test2 { public static void main(String[] args) { F a = new F(); F b = new D(); a.Func(); b.Func(); } }
输出结果为bd
作业四
public class Person { private String name = "Person"; int age = 0; } public class Child extends Person{ public String grade; public static void main(String[] args) { Person p = new Child(); System.out.println(p.name); } }
Person类中name的权限为private,该程序编译不通过
作业五
package Homework; import java.util.*; class Base3{ System.out.print("Base"); } class Alpha extends Base3{ } public class Test3 { public static void main(String[] args) { new Alpha(); //调用父类无参的构造方法 new Base3(); } }
打印结果为Base Base
作业六
package Homework; class X{ Y y = new Y(); public X(){ System.out.print("X"); } } class Y{ public Y(){ System.out.print("Y"); } } public class Z extends X{ Y y = new Y(); public Z(){ System.out.print("Z"); } public static void main(String[] args) { new Z(); } }
输出结果为YXYZ
输出步骤:静态的-》实例-》构造
作业七
super关键字只代表当前对象内部的那一块父类型特征,不包含在子类对象中
super关键字不仅可以指代子类的直接父类,还可以直接指代父类的父类
子类通过super关键字既能调用父类的方法,也可以调用父类的属性
作业八
多态的实现条件:
1.继承
2.向上转型
方法1:直接赋值
Anmials anmials = new Dog();
方法2:方法传参过程当中实现向上转型
public static void function2(Anmials anmials){ } public static void main(String[] args) { Dog dog = new Dog(); function2(dog); }
方法3:方法传参
public static Anmials function3(){ return new Dog(); }
3.重写