1.被private修饰的方法是不能被重写的
2.被final修饰的方法(密封方法)也不能被重写
3.被static修饰的也不能重写
区别点 | 重写 | 重载 |
方法名称 | 方法名称相同 | 相同 |
返回值 | 返回值相同(不同的话构成父子类关系也可以) | 不做要求 |
参数列表 | 参数列表相同(数据类型、顺序、个数) | 不同 |
重写中返回值构成父子类关系的示例代码:
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体 调用那个类的方法。
class Animal {
public void eat() {
System.out.println(name + "吃饭!");
}
}
class Dog extends Animal{
public void eat() {
System.out.println(name+"正在吃狗粮!");
}
}
在编译的时候还是Animal的eat(),但是在程序运行的时候就变成了Dog的eat(),这个过程我们就叫做:动态绑定。
public class TestAnimal {
// 2. 方法传参:形参为父类型引用,可以接收任意子类的对象
public static void eatFood(Animal a){
a.eat();
}
// 3. 作返回值:返回任意子类对象
public static Animal buyAnimal(String var){
if("狗".equls(var)){
return new Dog("狗狗",1);
}else if("猫"equls(var)){
return new Cat("猫猫", 1);
}else{
return null;
}
}
public static void main(String[] args) {
Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象
Dog dog = new Dog("小七", 1);
eatFood(cat);
eatFood(dog);
Animal animal = buyAnimal("狗");
animal.eat();
animal = buyAnimal("猫");
animal.eat();
}
}
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了instanceof ,如果该表达式为true,则可以安全转换。
class Animal {
String name;
int age;
public Animal(String name, int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(name + "吃饭");
}
}
class Cat extends Animal{
public Cat(String name, int age){
super(name, age);
}
@Override
public void eat(){
System.out.println(name+"吃鱼~~~");
}
public void mew(){
System.out.println(name + "喵喵喵叫");
}
}
class Dog extends Animal {
public Dog(String name, int age){
super(name, age);
}
@Override
public void eat(){
System.out.println(name+"吃骨头~~~");
}
public void bark(){
System.out.println(name + "汪汪汪汪叫");
}
}
public class Home0120{
public static void main(String[] args) {
Cat cat = new Cat("元宝",2);
Dog dog = new Dog("小七", 1);
// 向上转型
Animal animal = cat;
animal.eat();
animal = dog;
animal.eat();
if(animal instanceof Cat){
cat = (Cat)animal; cat.mew();
}
if(animal instanceof Dog){
dog = (Dog)animal; dog.bark();
}
}
}
在java中要实现多态,必须要满足如下几个条件,缺一不可:
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 Home0123 {
public static void drawMap(Shape shape){
shape.draw();
}
public static void main(String[] args) {
drawMap(new Rect());
drawMap(new Cycle());
drawMap(new Triangle());
}
}
在drawMap方法当中,Shape shape引用 引用的子类对象不一样,调用方法表现出来的行为不一样。我们把这种思想就叫做多态。
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
代码的运行效率会变低
我们创建两个类, B 是父类, D 是子类. D 中重写 func 方法. 并且在 B 的构造方法中调用 func
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 Test {
public static void main(String[] args) {
D d = new D();
}
}
构造 D 对象的同时, 会调用 B 的构造方法.B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func.此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0. 如果具备多态性,num的值应该是1. 所以在构造函数内,尽量避免使用实例方法,除了final和private方法。
结论: "用尽量简单的方式使对象进入可工作状态", 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题.