Java零基础学习Day14——面向对象进阶
面向对象三大特征:封装 继承 多态
一、多态
1.基本概念
多态:同类型的对象表现出的不同形态
表现形式:基类类型 对象名称 = 派生类对象;
前提:有继承关系、有基类引用指向派生类对象、有方法重写
优点:使用基类作为参数,可接收派生类对象
person student 、teacher、administractor
package orphismdemo1;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("Name: " + name+ " Age: " + age);}
}
package orphismdemo1;public class Student extends person {@Overridepublic void show() {System.out.println("学生信息为:"+"Name: " + getName() + " Age: " + getAge());}
}
package orphismdemo1;public class Teacher extends person {@Overridepublic void show() {System.out.println("老师信息为:"+"Name: " + getName() + " Age: " + getAge());}
}
package orphismdemo1;public class Administrator extends person {@Overridepublic void show() {System.out.println("管理员信息为:"+"Name: " + getName() + " Age: " + getAge());}
}
package orphismdemo1;public class Test {public static void main(String[] args) {Administrator a = new Administrator();a.setName("张三");a.setAge(20);Student s = new Student();s.setName("李四");s.setAge(27);Teacher t = new Teacher();t.setName("王五");t.setAge(18);register(a);register(s);register(t);}//该方法既能接收person类型的变量,又能接收person派生类的变量public static void register(person p){p.show();}
}
2. 多态中调用成员的特点
调用成员变量
编译看左边,运行看左边
在派生类对象中,会把基类的成员变量也继承下来
Animal a = new Dog();
编译看左边-javac编译时,检查左边的基类中有无此变量,有则编译成功,否则编译失败
运行看左边-java运行时,实际获取的是左边基类中成员变量的值
调用成员方法
编译看左边,运行看右边
若派生类对方法进行重写,那么在虚方法表中基类的方法会被覆盖
a.show();
编译看左边-javac编译时,检查左边基类中是否有此方法,有则编译成功,否则编译失败
运行看右边-java运行时,实际运行的是右边派生类中的成员方法
3. 多态的优势和弊端
优势
多态形式下,右边对象可实现解耦合,便于扩展和维护
Person p = new student();
p.work(); // 业务逻辑发生改变,不要学生去work,直接改变student()即可,后续代码都不用变
定义方法时,使用基类作为参数,可接收所有派生类对象
StringBuilder sb = new StringBuilder();
sb.append(); // append(Object obj), object是最上层的基类,此时所有类型的都可以添加到sb中
ArrayList list = new ArrayList();
list.add; // add(Object e)
弊端
不能调用子类的特有功能,
若要使用需要强制转换,需注意转换类型和真实对象的一致性,用instanceof进行判断
package orphismdemo1;public class Test {public static void main(String[] args) {Animal a = new Dog();a.eat();a.watchHome();// 报错,因为a的编译类型是Animal,Animal类中没有lookHome()方法Dog b = (Dog) a;//只能是Dog类型,不能是Cat类型及其他b.watchHouse();//判断a的运行类型是否是Dog、Catif (a instanceof Dog c) {c.watchHouse();}else if (a instanceof Cat d) {d.catchMouse();}else {System.out.println("目前没有这个类型,无法转换");}}
}
class Animal {public void eat() {System.out.println("动物进食");}
}
class Dog extends Animal {public void eat() {System.out.println("狗吃骨头");}public void watchHouse() {System.out.println("狗看家");}
}
class Cat extends Animal {public void eat() {System.out.println("猫吃鱼");}public void catchMouse() {System.out.println("猫抓老鼠");}
}
4. 综合练习
package orphismdemo2;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 d,String something){
// System.out.println(getAge()+"岁的"+getName()+"养了一只"+d.getAge()+"岁的"+d.getColor()+"颜色的狗");
// d.eat(something);
// }
// public void keepPet(Cat c,String something){
// System.out.println(getAge()+"岁的"+getName()+"养了一只"+c.getAge()+"岁的"+c.getColor()+"颜色的猫");
// c.eat(something);
// }public void keepPet(Animal a,String something){if(a instanceof Dog d){System.out.println(getAge()+"岁的"+getName()+"养了一只"+d.getAge()+"岁的"+d.getColor()+"颜色的狗");d.eat(something);}else if(a instanceof Cat c){System.out.println(getAge()+"岁的"+getName()+"养了一只"+c.getAge()+"岁的"+c.getColor()+"颜色的猫");c.eat(something);}else{System.out.println("没有这种动物");}}
}
package orphismdemo2;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");}
}
package orphismdemo2;public class Dog extends Animal {public Dog() {}public Dog(int age, String color) {super(age, color);}@Overridepublic void eat(String something){System.out.println(getAge()+"岁的"+getColor()+"颜色的狗两只前腿死死的保住"+something+"猛吃");}public void lookHome(){System.out.println("狗在看家");}
}
package orphismdemo2;public class Cat extends Animal {public Cat() {}public Cat(int age, String color) {super(age, color);}@Overridepublic void eat(String something){System.out.println(getAge()+"岁的"+getColor()+"颜色的猫眯着眼睛侧着头吃"+something);}public void catchMouse(){System.out.println("猫在抓老鼠");}
}
package orphismdemo2;
/*
根据需求完成代码: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 something)方法打印内容如下:
年龄为30岁的老王养了一只黑颜色的2岁的狗2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
keepPet(Cat cat,String something)方法打印内容如下:
年龄为25岁的老李养了一只灰颜色的3岁的猫3岁的灰颜色的猫眯着眼睛侧着头吃鱼5.思考:
1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keepPet方法,过于繁琐,能否简化,并体会简化后的好处?
2.Dog和Cat虽然都是Animal的子类,但是都有其特有方法,能否想办法在keepPet中调用特有方法?*/public class Test {public static void main(String[] args) {// Person p = new Person("老王", 30);
// Dog d = new Dog(2, "黑");
// p.keepPet(d, "骨头");
//
// Person p2 = new Person("老李", 25);
// Cat c = new Cat(3, "灰");
// p2.keepPet(c, "猫");Person p = new Person("老王", 30);Dog d = new Dog(2, "黑");Cat c = new Cat(3, "灰");p.keepPet(d, "骨头");p.keepPet(c, "鱼");}
}
二、包、final、权限修饰符、代码块
1. 包
定义:文件夹,用于管理各类功能不同的java类
规则:公司域名反写+包的作用,英文小写、见名知意
使用其他类的规则:使用同一个包里的类、java.lang包里的类(String),不需要导包
其他情况需要导包
若同时使用两个包里的同名类,需用全类名
com.itheima.domain.Student;(全类名=包名+类名)
com.itheima.domain.Student s = new com.itheima.domain.Student();
或
import com.itheima.domain.Student;
Student s = new Student();
2. final
final可修饰:方法、类、变量
方法 | 表明该方法为最终方法,不能被重写 |
类 | 表明该类为最终类,不能被继承 |
变量 | 常量,只能被赋值一次 通常作为系统的配置信息 命名规范:全部大写,多个单词之间用下划线隔开 该变量为基本类型,则变量存储的数据值不变 final double PI = 3.14; PI = 2; // 报错 该变量为引用类型,则变量存储的地址值不变,内部属性值可改变 final Student s = new Student("zhangsan",23); s = new Student(); // 报错 s.setName("lisi"); s.setAge(27); 字符串不能改变和final有关 |
在之前的学生管理系统中进行final常量例子练习
import java.util.ArrayList;
import java.util.Scanner;
public class StudentSystem {public static void startStudentSystem() {ArrayList<Student> list = new ArrayList<>();loop: while (true) {System.out.println("--------------欢迎来到Marian的学生管理系统-------------");System.out.println("1.添加学生");System.out.println("2.删除学生");System.out.println("3.修改学生");System.out.println("4.查询学生");System.out.println("5.退出");System.out.println("6.请输入你的选择:");Scanner sc = new Scanner(System.in);String choose = sc.next();switch (choose) {case "1" -> addStudent(list); // 新增学生case "2" -> deleteStudent(list); // 删除学生case "3" -> updateStudent(list); // 修改学生case "4" -> queryStudent(list); // 查询学生case "5" -> {System.out.println("退出");break loop;//System.exit(0);和break loop的作用类似} // 退出default -> System.out.println("没有这个选项,请重新输入!");}}}
添加final常量后的代码如下:
import java.util.ArrayList;
import java.util.Scanner;
public class StudentSystem {private static final String ADD_STUDENT = "1";private static final String REMOVE_STUDENT = "2";private static final String UPDATE_STUDENT = "3";private static final String QUERY_STUDENT = "4";private static final String EXIT = "5";public static void startStudentSystem() {ArrayList<Student> list = new ArrayList<>();loop: while (true) {System.out.println("--------------欢迎来到Marian的学生管理系统-------------");System.out.println("1.添加学生");System.out.println("2.删除学生");System.out.println("3.修改学生");System.out.println("4.查询学生");System.out.println("5.退出");System.out.println("6.请输入你的选择:");Scanner sc = new Scanner(System.in);String choose = sc.next();switch (choose) {case ADD_STUDENT -> addStudent(list); // 新增学生case REMOVE_STUDENT -> deleteStudent(list); // 删除学生case UPDATE_STUDENT -> updateStudent(list); // 修改学生case QUERY_STUDENT -> queryStudent(list); // 查询学生case EXIT -> {System.out.println("退出");break loop;//System.exit(0);和break loop的作用类似} // 退出default -> System.out.println("没有这个选项,请重新输入!");}}}
3. 权限修饰符
作用:用来控制一个成员能够被访问的范围
修饰的对象:成员变量、方法、构造方法、内部类
类型:4种,作用范围由小到大为private<空着不写<protected<public
规则:实际开发中用private和public,通常成员变量私有,方法公开
当方法中的代码抽取的是其他方法中的共性代码,则方法私有
同一个类 | 同一个包的不同类 | 不同包的子类 | 不同包的无关类 | |
private | √ | |||
空着不写 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
4. 代码块
局部代码块(已淘汰)
package CodeBlockDemo1;public class codeBlockDemo1 {public static void main(String[] args) {{int a = 1;}System.out.println(a);}
}
在{}结束后,int a从内存消失,节约内存
构造代码块(不够灵活)
位置:成员位置
作用:把多个构造方法中重复的代码抽取出来
执行时机:在创建本类对象时会先执行构造代码块再执行构造方法
package CodeBlockDemo1;public class Student {private String name;private int age;{System.out.println("开始创建对象了");}public Student() {// System.out.println("开始创建对象了");System.out.println("空参构造");}public Student(String name, int age) {// System.out.println("开始创建对象了");System.out.println("有参构造");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;}
}
package CodeBlockDemo1;public class codeBlockDemo1 {public static void main(String[] args) {Student s1 = new Student();System.out.println("-----------------------------------");Student s2 = new Student("zhangsan", 22);}
}
静态代码块(重点)
格式:static{}
特点:随着类加载而加载,自动触发、只执行一次
package CodeBlockDemo1;public class Student {private String name;private int age;static {System.out.println("静态代码块执行了");}public Student() {// System.out.println("开始创建对象了");System.out.println("空参构造");}public Student(String name, int age) {// System.out.println("开始创建对象了");System.out.println("有参构造");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;}
}
package CodeBlockDemo1;public class codeBlockDemo1 {public static void main(String[] args) {Student s1 = new Student();System.out.println("-----------------------------------");Student s2 = new Student("zhangsan", 22);}
}
使用场景:类加载时需数据初始化时
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
public class UserSystem {// 定义一个静态集合存储用户信息static ArrayList<User> list = new ArrayList<>();static {// 初始化集合,存储一些用户信息list.add(new User("lisi", "123456", "123456789012345678", "13800138000"));list.add(new User("zhangsan", "123qiqi", "123456789012345678", "13800138000"));}public static void main(String[] args) {
// ArrayList<User> list = new ArrayList<>();Scanner sc = new Scanner(System.in);loop:while (true) {System.out.println("--------------欢迎来到学生管理系统-------------");System.out.println("1.登录");System.out.println("2.注册");System.out.println("3.忘记密码");System.out.println("请选择操作:");String choose = sc.next();switch (choose) {case "1" -> login(list);case "2" -> register(list);case "3" -> forgetPassword(list);case "4" -> {System.out.println("谢谢使用,再见");break loop;//System.exit(0);和break loop的作用类似} // 退出default -> System.out.println("没有这个选项,请重新输入!");}}}