多态顾名思义多种形态
那么多态有什么用呢?
来看看下面这个例子:
如果有三个人:老师 学生 管理员它他们分别要要在学生管理系统上注册,调用三个函数的话也可以 以
public void register(Student s){
s.show();
}
但是如果人数很多呢?还一个一个这样创建吗?显然不合适.这就可以运用到多态的知识,还是以上面的三个人为例,他们都有共性都是人那么就可以运用继承里的知识点创建一个父类Person
多态的好处?
使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性和便利.
下面是代码验证上面所说的知识:
Person类:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println("人的信息为:"+getName()+", "+getAge());
}
}
学生类:
public class Student extends Person{
@Override
public void show(){
System.out.println("学生的信息为:"+getName()+", "+getAge());
}
}
老师类:
public class Teacher extends Person{
@Override
public void show(){
System.out.println("老师的信息为:"+getName()+", "+getAge());
}
}
管理员类:
public class Administrator extends Person{
@Override
public void show(){
System.out.println("管理员的信息为:"+getName()+", "+getAge());
}
}
Test类:
public class Test {
public static void main(String[] args) {
//这个方法既能接受老师,又能接受学生把参数学成这个三个类型的父类
Student s =new Student();
s.setName("yjy");
s.setAge(18);
Teacher t = new Teacher();
t.setName("Mrs.white");
t.setAge(25);
Administrator admin = new Administrator();
admin.setAge(30);
admin.setName("joy");
//创建完三个对象,并且调用register方法
register(s);
register(t);
register(admin);
}
public static void register (Person p){
p.show();
}
}
运行结果:
变量调用:编译看左边,运行也看左边.
方法调用:编译看左边,运行看右边.
package a02polymorphismdemo2;
public class Test {
public static void main(String[] args) {
//创建方式(多态方式)
//Fu f =new Zi();
Animal a =new Dog();
//用多态方式调用成员变量:编译看左边,运行也看左边
//编译看左边:Java编译代码的时候,会看左边的父类中有没有这个变量,如果有则编译成功,如果没有则编译失败
//如果把父类中 String name="动物";删除的话 就编译失败 程序根本无法运行
//运行也看左边:java运行代码的时候,实际获取的就是左边父类成员变量的值
System.out.println(a.name);//动物
//调用成员方法:编译看左边,运行看右边
//编译看左边:Java编译代码的时候,会看左边的父类中有没有这个变量,如果有则编译成功,如果没有则编译失败
//如果把父类中public void show()注释掉 那么将编译失败 程序无法运行
//运行看右边:java运行代码的时候,实际运行的是子类中的方法
a.show();//Dog ---show方法
//理解:
//Animal a =new Dog();
//现在用a去调用变量和方法
//而a是Animal类型的,所以默认都会是Animal这个类中找
//成员变量:在子类对象中,会把父类中的成员变量也继承下来的.父:name 子:name
//以前我们是这样调用的:Dog d =new Dog();也就是用d去调用成员变量 然后他的类型是
//Dog因此的到的也就是Dog的成员变量
//现在是Animal类型 因此得到的也就是Animal类型的成员变量
//成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的.
//什么是虚方法表?继承那篇文章有说明,也就是非static 非final 非private
}
}
class Animal{
String name="动物";
public void show(){
System.out.println("Animal --- show方法");
}
}
class Dog extends Animal{
String name ="狗";
@Override
public void show(){
System.out.println("Dog --- show方法");
}
}
class Cat extends Animal{
String name ="猫";
@Override
public void show(){
System.out.println("Cat ---show方法");
}
}
在多态形势下:右边对象可以实现解耦合,便于扩展和维护.
Person p = new Student();
p.work();//业务逻辑发生改变时,后续代码无需修改
定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体系多态的扩展性和便利.
可以把任意的对象都往里面放 这也是多态的一个优势
public class Test {
public static void main(String[] args) {
//ArrayList list =new ArrayList<>();
//这里表示只能把字符串往里面添加不能添加其他的
//如果不写泛型 也就是下面这样
ArrayList list =new ArrayList();
//这就表示所有的类型都可以往里面添加
}
}
弊端:(代码形式分析)
package a02polymorphismdemo2;
public class Test {
public static void main(String[] args) {
//创建对象
Animal a =new Dog();
//编译看左边,运行看右边
a.eat();//狗吃骨头
//多态的弊端:
//不能调用子类的特有功能
//a.lookHome();
//报错的原因?
//当调用成员方法的时候,编译看左边,运行看右边
//那么在编译的时候先检查左边父类中有没有这个方法,如果没有直接报错
//解决方案:
//变回子类类型就可以了
// int b=10;
// byte c =(byte)b;
Dog d = (Dog) a;//大变小alt +回车
d.lookHome();
//细节:转换的时候不能瞎转,如果转成其他类的类型就会报错
// Cat c =(Cat) a;//狗不能转成猫
// c.catchMouse();
// if(a instanceof Dog){
// Dog c =(Dog)a;
// //a是不是狗
// }else if(a instanceof Cat){
// Cat c =(Cat)a;
// }else{
// System.out.println("没有这个类型,无法转换");
// }
//新特性:
//先判断a是否为Dog类型 如果是 则强转成Dog类型 转换之后变量名为a
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
}
}
class Animal {
public void eat(){
System.out.println("动物在吃东西");
}
}
class Dog extends Animal{
@Override
public void eat(){
System.out.println("狗吃骨头");
}
public void lookHome(){
System.out.println("狗看家");
}
}
class Cat extends Animal{
@Override
public void eat(){
System.out.println("猫吃小鱼干");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
多态的综合练习
根据需求完成代码:
1.定义狗类
属性:
年龄,颜色
行为:
eat(String something)(something表示吃的东西)
看家lookHome方法(无参数)
2.定义猫类
属性:
年龄,颜色
行为:
eat(String something)方法(something表示吃的东西)
逮老鼠catchMouse方法(无参数)
3.定义Person类//饲养员
属性:
姓名,年龄
行为:
keepPet(Dog dog,String something)方法
功能:喂养宠物狗,something表示喂养的东西
行为:
keepPet(Cat cat,String something)方法
功能:喂养宠物猫,something表示喂养的东西
生成空参有参构造,set和get方法
4.定义测试类(完成以下打印效果):
keepPet(Dog dog,String somethind)方法打印内容如下:
年龄为30岁的老王养了一只黑颜色的2岁的狗
2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
keepPet(Cat cat,String somethind)方法打印内容如下:
年龄为25岁的老李养了一只灰颜色的3岁的猫
3岁的灰颜色的猫眯着眼睛侧着头吃鱼
5.思考:
1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keepPet方法,过于繁琐,能否简化,并体会简化后的好处?
2.Dog和Cat虽然都是Animal的子类,但是都有其特有方法,能否想办法在keepPet中调用特有方法?
Animal类:
public class Animal {
private int age;
private String color;
public Animal() {
}
public Animal(int age, String color) {
this.age = age;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void eat(String something){
System.out.println("动物在吃"+something);
}
}
在Java继承中我们说过:
cat类:
public class Cat extends Animal{
public Cat() {
}
public Cat(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge()+"岁的"+getColor()+"颜色的猫眯着眼睛侧着头吃"+something);
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
Dog类:
public class Dog extends Animal{
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge()+"岁的"+getColor()+"颜色的狗两只前腿死死的抱住"+something+"猛吃");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
Person类:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//饲养狗
// public void keepPet(Dog dog,String something){
// System.out.println("年龄为"+age+"岁的"+name+"养了一只"+dog.getColor()+"颜色的"+dog.getAge()+"岁的狗");
// dog.eat(something);
// }
// public void keepPet(Cat cat,String something){
// System.out.println("年龄为"+age+"岁的"+name+"养了一只"+cat.getColor()+"颜色的"+cat.getAge()+"岁的猫");
// cat.eat(something);
//想要一个方法,能够接收所有的动物,包括猫,包括狗
//方法的形参,可以写这些类,Animal
public void keePet(Animal a,String something){
if(a instanceof Dog d){
System.out.println("年龄为"+age+"岁的"+name+"养了一只"+d.getColor()+"颜色的"+d.getAge()+"岁的狗");
d.eat(something);
}
else if(a instanceof Cat c){
System.out.println("年龄为"+age+"岁的"+name+"养了一只"
+c.getColor()+"颜色的"+c.getAge()+"岁的动物");
c.eat(something);
}else{
System.out.println("没有这种动物");
}
}
}
Test:
public class Test {
public static void main(String[] args) {
//创建对象并调用方法
// Person p1 =new Person("老王",30);
// Dog d =new Dog(2,"黑");
// p1.keepPet(d,"骨头");
//
// Person p2=new Person("老李",25);
// Cat c =new Cat(3,"灰");
// p2.keepPet(c,"鱼");
//创建饲养员对象
Person p1 =new Person("老王",30);
Dog d =new Dog(2,"黑");
Cat c =new Cat(3,"灰");
p1.keePet(d,"骨头");
p1.keePet(c,"鱼");
}
}
包就是文件夹.用来管理不同功能的Java类,方便后期代码维护
包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意.com.yjy.domain
pack com.yjy.domain
public class Student{
私有化成员变量
构造方法 -------------------------> com.yjy.domain.Student(全类名,全限定名)
成员方法
}
使用其他类的规则
使用其他类时,需要使用全类名.
public class Test {
public static void main(String[] args) {
com.yjy.donmain.Student s =new com.yjy.donmain.Student();
}
}
import com.yjy.donmain.Student;
public class Test {
public static void main(String[] args) {
Student s =new Student();
}
}
使用同一个包的类时,不需要导包
使用java.lang包中的类时,不需要导包
其他情况都需要导包
如果同时使用两个包中的同名类,需要用全类名
最终的----------->不可改变的
方法:表明该方法是最终方法,不能重写
类:表明这个类是最终类,不能被继承
变量:叫做常量,只能被赋值一次
实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性.
常量的命名规范:
单个单词:全部大写
多个单词:全部大写,单词之间用下划线隔开
细节:
final修饰的变量是基本类型,那么变量存储的数据值不能发生改变
final修饰的变量是引用类型,那么变量存储的地址值不能发生改变,对象内部的可以改变
权限修饰符:是用来控制一个成员能够被访问的范围的.
可以修饰成员变量,方法,构造方法,内部类
private:只能自己用
默认/缺省:只能在本包用
protected:受保护的
public:公共的
局部代码块
{}:提前结束变量的生命周期.这个技术现在已经用不到了,因为现在计算机的硬件很强大 内存很大
构造代码块
作用:把重复代码放到构造代码块中,写在成员位置的代码块 执行时机:优先于构造方法执行
这个技术也渐渐淘汰了 这种写法不够灵活 因为只要卸载构造代码块中就一定会执行 太死板
可以这么写:
静态代码块
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
使用场景:在类加载的时候,做一些数据初始化的时候使用
数据初始化:在学生管理系统中