代码当中体现多态性,其实就是一句话:父类引用指向子类对象
格式:
父类名称 对象名 = new 子类名称();
或者:
接口名称 对象名 = new 实现类名称();
new谁就调用谁的方法(没有向上找),引用是谁,就用谁的成员和静态。
花木兰打仗用的是自己的方法,用的是父亲的信息.
口诀:编译(是否变红)看左,运行看右
成员变量:编译看左边,运行还看左边
成员方法:编译看左,运行看右边
多态存在要有3个必要条件:
1、继承
2、方法重写
3、父类引用指向子类对象。
public class Fu {
public void method(){
System.out.println("父类方法");
}
public void methodFu() {
System.out.println("父类特有的方法");
}
}
public class Zi extends Fu {
@Override
public void method(){
System.out.println("子类方法");
}
}
public class Demo01Multi {
public static void main(String[] args) {
//使用多态的写法
//左侧父类的引用,指向了右侧子类的对象
Fu obj = new Zi();
//编译看左(看Fu类中是否有method),运行看右(运行的是Zi类的method)
obj.method();//子类方法
obj.methodFu();//父类特有的方法
}
}
动物的例子
public abstract class Animal {
public abstract void eat();
}
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
//子类特有方法
public void catchMouse(){
System.out.println("抓老鼠");
}
}
public class Dog extends Animal {
@Override
public void eat(){
System.out.println("骨头");
}
public void watchHouse(){
System.out.println("看家");
}
}
/*
向上转型就是多态的使用,多态就是向上转型
向上转型一定是安全的,但有一个弊端:
对象一旦向上转型为父类,那么就无法调用子类原本特有的内容
解决方案:用对象的向下转型【还原】。
*/
public class Main {
public static void main(String[] args) {
//对象的向上转型,就是:父类引用指向子类对象
Animal animal = new Cat();
animal.eat();//猫吃鱼
//就是.eat()时先看Animal类中是否有eat()方法,
//有就可以,出来
//但是用eat()的时候,用的时new出来的对象Cat的方法
/*animal.catchMouse();//错误写法!*/
//因为Animal animal = new Cat();是将猫当动物来看的
//不是所有的动物都有catchMouse()方法
//向下转型,进行”还原“动作
Cat cat = (Cat) animal;
cat.catchMouse();//抓老鼠
//下面是错误的向下转型
//本来new的是一只猫,现在非要当狗
Dog dog = (Dog) animal;//错误写法,编译不会报错,但运行会出现异常
//ClassCastException
}
}
Instanceof
/*
如何才能知道一个父类引用的对象,本来是什么子类?
格式:
对象 instanceof 类名称
这将会得到一个boolean值的结果,也就是判断前面的对象能不能当作后面类型的实例
*/
public class Instanceof {
public static void main(String[] args) {
Animal animal = new Dog();
animal.eat();
//如果希望调用子类特有的方法,需要向下转型
//判断一下父类引用animal本来是不是Dog
if(animal instanceof Dog){
Dog dog = (Dog) animal;
dog.watchHouse();
}
//判断一下animal本来是不是Cat
if(animal instanceof Cat){
Cat cat = (Cat) animal;
cat.catchMouse();
}
giveMe_A_Pet(new Dog());
}
public static void giveMe_A_Pet(Animal animal){
if(animal instanceof Dog){
Dog dog = (Dog) animal;
dog.watchHouse();
}
//判断一下animal本来是不是Cat
if(animal instanceof Cat){
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
}
final关键字代表最终、不可改变的。
常见四种用法:
1、可以用来修饰一个类
2、可以用来修饰一个方法
3、还可以用来修饰一个局部变量
4、还可以用来修饰一个成员变量
当final关键字用来修饰一个类的时候,格式:
public final class 类名称{
//...
}
含义:
当前这个类不能有任何的子类
即final类不能作为父类。
注意:
一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写(因为没有儿子。)
当final关键字用来修饰一个方法的时候,格式:
修饰符 final 返回值类型 方法名称(参数列表){
//方法体
}
含义:
这个方法就是最终方法,不能被覆盖重写。
注意:
对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾。
public class Fu {
public void method(){
System.out.println("父类方法执行");
}
public final void methodFinal(){
System.out.println("最终方法,没有办法被子类覆盖重写");
}
}
public class Zi extends Fu {
@Override
public void method(){
System.out.println("子类覆盖重写了这个方法");
}
}
一旦使用final用来修饰局部变量,那么这个变量就不能进行更改。
对于基本类型来说,不可变说的是变量当中的数据不可改变
对于引用类型来说,不可变说的是变量当中的地址值不可改变
public class Demo01Final {
public static void main(String[] args) {
//一旦使用final用来修饰局部变量,那么这个变量就不能进行更改。
final int num = 10;
//正确写法!只能保证有唯一一次赋值即可
final int num1;
num1 = 30;
//对于基本类型来说,不可变说的是变量当中的数据不可改变
//对于引用类型来说,不可变说的是变量当中的地址值不可改变
Student stu = new Student("凡星");
System.out.println(stu);//@2d98a335(地址值)
System.out.println(stu.getName());//凡星
stu = new Student("fx");
System.out.println(stu);//@16b98e56(地址值)
System.out.println(stu.getName());//fx
System.out.println("==========================");
final Student stu1 = new Student("hh");
//错误写法,final的引用类型变量,其中的地址不可改变
// stu1 = new Student("xx");
stu1.setName("xx");
stu1.getName();
System.out.println(stu1.getName());//xx
}
}
成员变量,如果使用final关键字修饰,那么这个变量也照样是不可变。
1、由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了
2、对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值
3、必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值
/*
一个标准的类,包含以下几个东西
1、私有的成员变量
2、无参构造
3、全参构造
4、getter、setter
以上除1自己写,其他用Alt+Insert快捷键
*/
public class Student {
private final String name = "fx";
public > | protected > | (default) > | private | |
---|---|---|---|---|
同一个类 | YES | YES | YES | YES |
同一个包 | YES | YES | YES | NO |
不同包子类 | YES | YES | NO | NO |
不同包非子类 | YES | NO | NO | NO |
(1)私有权限 private:private可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类)。被private修饰的成员,只能在定义它们的类中使用,在 其他类中不能调用。
(2)默认权限 default:类,数据成员,构造方法,方法成员,都能够使用默认权限,即不写任何关键字。默认权限即同包权限,同包权限的元素只能在定义它们的类中,以及同包 的类中被调用。
(3)受保护权限 protected:protected可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类)。被protected修饰的成员,能在定义它们的类中,同包 的类中被调用。如果有不同包的类想调用它们,那么这个类必须是定义它们的类的子类。
(4)公共权限 public:public可以修饰类,数据成员,构造方法,方法成员。被public修饰的成员,可以在任何一个类中被调用,不管同包或不同包,是权限最大的一个修饰符。
注意:
(1)并不是每个修饰符都可以修饰类(指外部类),只有public和default可以。
(2)所有修饰符都可以修饰数据成员,方法成员,构造方法。
(3)为了代码安全起见,修饰符不要尽量使用权限大的,而是适用即可。比如,数据成员,如果没有特殊需要,尽可能用private。
(4)修饰符修饰的是“被访问”的权限。