目录
1.继承
1.1关键字super
1.2关键字protected
1.3关键字final
1.4组合
//小练习:三者乘积
2.多态
2.1重写override
2.2向上转型与向下转型
2.3运行时绑定
2.4多态
定义:对子类的共性进行抽取并放到父类当中。
优点:达到了代码的复用。
语法:class + 子类/派生类 + extends + 父类/基类/超类
class Animal{ //父类
public String name;
public int age;
public void eat() {
System.out.println(this.name+"正在吃饭");
}
}
class Dog extends Animal{ //子类
public String field;//子类自己的
public void bark() {
System.out.println(this.name+"汪汪汪");
}
}
class Cat extends Animal{ //子类
public void bark() {
System.out.println(this.name+"喵喵喵");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "灰灰";
dog.age = 4;
dog.bark();
Cat cat = new Cat();
cat.name = "咪咪";
cat.age = 3;
cat.bark();
}
}
1. 当子类和父类成员变量重名时,优先访问子类自己的,若想先访问父类成员,在前面加上关键字super;
2.super只能在子类中使用,增加代码的可读性,并不是父类对象的引用;
2. super 和 this 都只能在静态方法中使用;super 与 this 在构造方法中调用时,必须都是第一条语句,所以不能同时存在。
class Base {
public int a = 1;
public int b = 2;
public void method() {
System.out.println("父类method");
}
}
class Derived extends Base {
public int a = 3;//与父类重名了
public int c = 4;
public void method() {
System.out.println(a);//3 —— 重名时优先使用子类自己的
System.out.println(super.a);//1 —— 强制优先使用父类
System.out.println(b);//2
System.out.println(c);//4
super.method();//父类method
}
}
public class TestDemo {
public static void main(String[] args) {
Derived derived = new Derived();
derived.method();
}
}
注意:子类在完成构造前,必须先完成父类的构造!(利用super)
若父类中没有写构造方法,编译器会自动在父类和子类中生成不带参数的构造方法,但若父类中写了带参数的构造方法,子类中就必须写代码块帮助父类构造。
class Animal {
public String name;
public int age;
public Animal(String name, int age) { //父类中有构造方法
this.name = name;
this.age = age;
}
}
class Dog extends Animal {
String habit;
public Dog() { //子类构造方法
super("huihui",3);//帮助父类构造
}
public Dog(String name,int age) { //子类构造方法
super(name,age);
}
public void bark() {
System.out.println(this.name+"汪汪汪");
}
}
访问权限:同一包中的子类和非子类,不同包中的子类。
同一包中的访问很简单,这里就不赘叙了,下面来看看不同包中如何访问:
package Demo;
public class Test1 {
protected int a = 10;
}
package Demo2;
import Demo.Test1;
public class Test2 extends Test1 { //继承
public void func() {
System.out.println(super.a);//通过super访问
}
public static void main(String[] args) {
Test3 test3 = new Test2();
test2.func();//10
}
}
如果一个类被final修饰了,那么这个类就不能被继承了;
如果一个变量被final修饰了,那么这个变量就不能被修改了(相当于const);
分类:单继承,不同类继承同一个类,多继承(Java不支持)
final class Math { //final修饰后该类不能被继承
public int m = 1;
public int n = 2;
}
public class Test {
public static void main(String[] args) {
final int a = 10;//此时a相当于常量
//a = 100;//不能被修改
System.out.println(a);
final int b;//定义的同时可以不初始化
b = 20;
//b = 200;//但只能初始化一次
System.out.println(b);
}
}
继承与组合的区别:https://www.hollischuang.com/archives/1319
继承表示对象之间是is-a的关系:狗是动物;
组合表示对象之间是has-a的关系:汽车与零部件,学校与师生。
class Student{
}
class Teacher{
}
class School{
private Student[] students;
private Teacher[] teachers;
}
有父类Base,内部定义了x、y属性。有子类Sub,继承自父类Base。子类新增了一个z属性,并且定义了calculate方法,在此方法内计算了父类和子类中x、y、z属性三者的乘积。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextInt()) {
int x = scanner.nextInt();
int y = scanner.nextInt();
int z = scanner.nextInt();
Sub sub = new Sub(x, y, z);
System.out.println(sub.calculate());
}
}
}
class Base {
private int x;
private int y;
public Base(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
class Sub extends Base {
private int z;
public Sub(int x, int y, int z) {
super(x,y);
this.z = z;
}
public int getZ() {
return z;
}
public int calculate() {
return super.getX() * super.getY() * this.getZ();
}
}
条件:方法名称,参数列表(个数,顺序,类型),返回类型均相同。
注意:1> 被 private,static,final修饰的方法 [ 密封方法 ] 以及构造方法不能被重写;
2> 子类的访问修饰限定符一定要大于等于父类的访问修饰限定符;
3> 重写的返回值类型可以不同,但必须是父子类关系(协变类型);
4> 可手动加上@Override注解来显式指定,帮我们进行合法性校验。
class Animal {
public String name;
public int age;
public void eat() {
System.out.println(this.name+"正在吃饭!");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Dog extends Animal {
public Dog(String name,int age) {
super(name,age);
this.name = name;
this.age = age;
}
@Override
public void eat() { //与父类的eat方法构成了重写
System.out.println(this.name+"正在吃狗粮!");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("wangwang",1);
dog.eat();
}
}
定义:用父类引用来接受子类对象,从小范围向大范围的转换。
语法:父类类型 对象名 = new 子类类型( );
发生的时机: 直接赋值、传参、返回值。
优点:让代码更简单灵活;缺点:不能调用到子类特有的方法。
public class Test {
public static void main(String[] args) {
/*Dog dog = new Dog("wangwang",1);
Animal animal = dog;*/
Animal animal = new Dog("wangwang",1);//等价于上面两行代码,即向上转型
}
}
向下转型:不安全,要使用关键字 instanceof 来判断能否转型
public class Test {
public static void main(String[] args) {
Animal animal = new Dog("wangwang",1);
if (animal instanceof Cat) { //判断能否转型
Cat cat = (Cat) animal;//向下转型
cat.mimi();//编译通过,运行报错,不安全
}
if (animal instanceof Dog) {
Dog dog = (Dog) animal;//向下转型
dog.bark();//编译通过,运行不报错
}
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Dog("wangwang",1);
//animal.bark();//类型为Animal,animal无法调用子类的bark方法,只能调用父类自己的方法
animal.eat();//同上,按理说应当调用父类的eat方法-吃饭,但实际上调用了子类的eat方法-吃狗粮,why??
}
}
通过查看汇编代码,我们发现,编译时确实调用了父类Animal的eat方法,但是运行的结果又是子类Dog的。这个过程就叫做 运行时绑定 / 动态绑定。
触发条件:1> 发生了重写;2> 通过父类引用调用了重写的方法。
动态绑定:后期绑定(晚绑定),即在编译时不能确定方法的行为,需要等到程序运行时才能确定具体调用哪个方法;
静态绑定:前期绑定(早绑定),即在编译时根据用户所传递的参数类型个数返回值等就确定了具体调用哪个方法,如重载。
父类引用子类对象,当引用的子类对象不一样的时候,通过这个父类引用调用父类和子类重写方法时,此时同一个引用呈现出来的不同状态,就是多态。
条件:1> 必须在继承体系下;
2> 子类必须要对父类中的方法进行重写;
3> 通过父类的引用调用重写方法。
public class Test {
public static void testFunc(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
Dog dog = new Dog("wangwang",1);
testFunc(dog);
System.out.println("----------");
Cat cat = new Cat("miaomiao",2);
testFunc(cat);
}
}
优点:降低代码的“圈复杂度”,避免使用大量的if-else语句。
缺点:代码的运行效率降低,属性没有多态性。
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 Flower extends Shape{
@Override
public void draw() {
System.out.println("❀");
}
}
public class Demo {
public static void drawShapes1() { //不基于多态
Rect rect = new Rect();
Cycle cycle = new Cycle();
Flower flower = new Flower();
String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};
for (String shape : shapes) { //有大量的if-else语句
if (shape.equals("cycle")) {
cycle.draw();
} else if (shape.equals("rect")) {
rect.draw();
} else if (shape.equals("flower")) {
flower.draw();
}
}
}
public static void drawShapes2() { //基于多态,代码更加简洁
Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),
new Rect(), new Flower()};
for (Shape shape : shapes) {
shape.draw();
}
}
public static void main(String[] args) {
drawShapes2();
}
}
Over!接口见!!