三、多态
#什么是多态?
同类型的对象,表现出的不同形态。
#多态的表现形式
父类类型 对象名称 = 子类对象;
#多态的前提
●有继承关系
●有父类引用指向子类对象
●有方法重写
#多态的好处?
使用父类型作为参数,可以接收所有子类对象,
体现多态的扩展性与便利。
#多态调用成员的特点
变量调用:编译看左边,运行也看左边。
方法调用:编译看左边,运行看右边。
#多态的优势
在多态形式下,右边对象可以实现解耦合,便于扩展和维护
定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利。
#多态的弊端是什么?
不能使用子类的特有功能
#引用数据类型的类型转换,有几种方式?
自动类型转换、强制类型转换
#强制类型转换能解决什么问题?
可以转换成真正的子类类型,从而调用子类独有功能。
转换类型与真实对象类型不一致会报错
转换的时候用instanceof关键字进行判断
##多态练习
1.需求文档
根据需求完成代码:
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中调用特有方法?
Person.Java
package cn.nwafu.c01polymorphismdemo03;
public class Person {
//3.定义Person类//饲养员
// 属性:
// 姓名,年龄
// 生成空参有参构造,set和get方法
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;
}
// 行为:
// keepPet(Dog dog,String something)方法
// 功能:喂养宠物狗,something表示喂养的东西
// 年龄为30岁的老王养了一只黑颜色的2岁的狗
// 行为:
// keepPet(Cat cat,String something)方法
// 功能:喂养宠物猫,something表示喂养的东西
// 年龄为25岁的老李养了一只灰颜色的3岁的猫
//饲养狗
// 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 keepPet(Animal a,String something){
if (a instanceof Dog d) {
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + a.getColor() + "颜色的" + a.getAge() + "岁的狗");
d.eat(something);
}else if(a instanceof Cat c){
System.out.println("年龄为"+age+"岁的"+name+"养了一只"+a.getColor()+"颜色的"+a.getAge()+"岁的猫");
c.eat(something);
}else{
System.out.println("没有这种动物!");
}
}
}
Animal.Java
package cn.nwafu.c01polymorphismdemo03;
public class Animal {
// 属性:
// 年龄,颜色
// 行为:
// eat(String something)(something表示吃的东西)
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);
}
}
Cat.Java
package cn.nwafu.c01polymorphismdemo03;
public class Cat extends Animal{
//2.定义猫类
// 逮老鼠catchMouse方法(无参数)
public Cat() {
}
public Cat(int age, String color) {
super(age, color);
}
//行为
//3岁的灰颜色的猫眯着眼睛侧着头吃鱼
@Override
public void eat(String something){
System.out.println(getAge()+"岁的"+getColor()+"颜色的猫眯着眼睛侧着头吃"+something);
}
public void catchMouse(){
System.out.println("猫在逮老鼠");
}
}
Dog.Java
package cn.nwafu.c01polymorphismdemo03;
public class Dog extends Animal {
// 1.定义狗类
// 看家lookHome方法(无参数)
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
//行为
// 2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
@Override
public void eat(String something){
System.out.println(getAge()+"岁的"+getColor()+"颜色的狗两只前腿死死的抱住"+something+"猛吃");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
Test.Java
package cn.nwafu.c01polymorphismdemo03;
public class Test {
//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中调用特有方法?
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);
Person p2 = new Person("老李",25);
Dog d = new Dog(2,"黑");
d.lookHome();//创建对象后可以调用特有方法
Cat c = new Cat(3,"灰");
c.catchMouse();
p1.keepPet(d,"骨头");
p2.keepPet(c,"鱼");
}
}
四、包、final、权限修饰符、代码块
1.包
#包的作用:
包就是文件夹。用来管理各种不同功能的Java类,方便后期代码维护。
#包名书写规则
包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意。
#什么是全类名?
包名+类名
#使用其他类的规则
使用同一个包中的类时,不需要导包。
使用java.lang包中的类时,不需要导包。
其他情况都需要导包。
如果同时使用两个包中的同名类,需要用全类名。
2.final
方法 表明该方法是最终方法,不能被重写
类 表明该类是最终类,不能被继承
变量 叫做常量,只能被赋值一次
#常量
实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性。
常量的命名规范:
单个单词:全部大写
多个单词:全部大写,单词之间用下划线隔开
细节:
final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,对象内部的可以改变。
练习、常量的练习
将学生管理系统中用户的操作改写为常量的形式
StudentsStandardEdition.Java
package cn.nwafu;
public class StudentsStandardEdition {
private String id;
private String name;
private int age;
private String address;
public StudentsStandardEdition() {
}
public StudentsStandardEdition(String id, String name, int age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
/**
* 获取
* @return id
*/
public String getId() {
return id;
}
/**
* 设置
* @param id
*/
public void setId(String id) {
this.id = id;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 设置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
}
StudentSystemStandardEdition.Java
package cn.nwafu;
import java.util.ArrayList;
import java.util.Scanner;
public class StudentSystemStandardEdition {
private static final String ADD_STUDENT ="1";
private static final String DELETE_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 main(String[] args) {
ArrayList<StudentsStandardEdition> list = new ArrayList<>();
loop : while (true) {//标号外循环
System.out.println("-----------------------------------欢迎来到学生管理系统--------------------------------");
System.out.println("1:添加学生");
System.out.println("2:删除学生");
System.out.println("3:修改学生");
System.out.println("4:查询学生");
System.out.println("5:退出");
System.out.println("请输入您的选择:");
Scanner sc = new Scanner(System.in);
String choose = sc.next();
switch (choose) {//增加可读性
case ADD_STUDENT -> addStudent(list);
case DELETE_STUDENT -> deleteStudent(list);
case UPDATE_STUDENT -> updateStudent(list);
case QUERY_STUDENT -> queryStudent(list);
case EXIT -> {
System.out.println("退出");
//break只能跳出内循环
//如果想跳出外循环,有两种办法:
//1.将外循环标号,break 标号 跳出指定的外循环
break loop;
//2.如下面这个语句,直接停止虚拟机的运行
//System.exit(0);
}
default -> System.out.println("没有这个选项");
}
}
}
//添加学生
public static void addStudent(ArrayList<StudentsStandardEdition> list){
//创建一个学生对象并且把信息放入其中
StudentsStandardEdition stu = new StudentsStandardEdition();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入学生的id");
String id =sc.next();
boolean flag = contains(list,id);
if(flag){
System.out.println("当前id已存在,请重新录入");
}else {
stu.setId(id);
break;
}
}
System.out.println("请输入学生的姓名");
String name =sc.next();
stu.setName(name);
System.out.println("请输入学生的年龄");
int age =sc.nextInt();
stu.setAge(age);
System.out.println("请输入学生的家庭地址");
String address =sc.next();
stu.setAddress(address);
//把学生对象添加到集合当中
list.add(stu);
//提示一下用户
System.out.println("学生信息添加成功!");
}
//删除学生
public static void deleteStudent(ArrayList<StudentsStandardEdition> list){
Scanner sc = new Scanner(System.in);
System.out.println("请输入要删除的id");
String id = sc.next();
if (contains(list,id)){
int index = getIndex(list,id);
list.remove(index);
System.out.println("id为:"+id+"的学生删除成功!");
}else {
System.out.println("当前id不存在,操作失败!");
}
}
//修改学生
public static void updateStudent(ArrayList<StudentsStandardEdition> list){
Scanner sc = new Scanner(System.in);
System.out.println("请输入想要修改学生的id");
String id = sc.next();
int index = getIndex(list,id);
if (index >= 0){
StudentsStandardEdition stu = list.get(index);
System.out.println("请输入要修改的学生姓名");
String newName = sc.next();
stu.setName(newName);
System.out.println("请输入要修改的学生年龄");
int newAge = sc.nextInt();
stu.setAge(newAge);
System.out.println("请输入要修改的学生家庭住址");
String newAddress = sc.next();
stu.setAddress(newAddress);
System.out.println("学生信息修改成功!");
}else{
System.out.println("当前id不存在,修改失败!");
}
}
//查询学生
public static void queryStudent(ArrayList<StudentsStandardEdition> list){
if (list.size() == 0){
System.out.println("当前无学生信息,请添加后再查询");
}else {
System.out.println("id\t\t姓名\t\t年龄\t家庭住址");
for (int i = 0; i < list.size(); i++) {
StudentsStandardEdition stu = list.get(i);
System.out.println(stu.getId() + "\t\t" + stu.getName() + "\t\t" + stu.getAge() + "\t" + stu.getAddress());
}
}
}
//判断当前id是否存在
public static boolean contains(ArrayList<StudentsStandardEdition> list, String id){
// for (int i = 0; i < list.size(); i++) {
// Student stu = list.get(i);
// String sid = stu.getId();
// if(sid.equals(id)){
// return true;
// }
// }
return getIndex(list,id) >= 0;
}
//通过id获取索引的方法
public static int getIndex(ArrayList<StudentsStandardEdition> list, String id){
for (int i = 0; i < list.size(); i++) {
String sid = list.get(i).getId();
if(sid.equals(id)){
return i;
}
}
return -1;
}
}
User.Java
package cn.nwafu;
public class User {
private String userName;
private String password;
private String idNumber;
private String phoneNumber;
public User() {
}
public User(String userName, String password, String idNumber, String phoneNumber) {
this.userName = userName;
this.password = password;
this.idNumber = idNumber;
this.phoneNumber = phoneNumber;
}
/**
* 获取
* @return userName
*/
public String getUserName() {
return userName;
}
/**
* 设置
* @param userName
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* 获取
* @return password
*/
public String getPassword() {
return password;
}
/**
* 设置
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
/**
* 获取
* @return idNumber
*/
public String getIdNumber() {
return idNumber;
}
/**
* 设置
* @param idNumber
*/
public void setIdNumber(String idNumber) {
this.idNumber = idNumber;
}
/**
* 获取
* @return phoneNumber
*/
public String getPhoneNumber() {
return phoneNumber;
}
/**
* 设置
* @param phoneNumber
*/
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String toString() {
return "StudentUpdatedVersion{userName = " + userName + ", password = " + password + ", idNumber = " + idNumber + ", phoneNumber = " + phoneNumber + "}";
}
}
Updated.Java
package cn.nwafu;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
public class Updated {
private static final int LOGIN = 1;
private static final int REGISTER = 2;
private static final int FORGET_PASSWORD = 3;
private static final int EXIT = 4;
public static void main(String[] args) {
ArrayList<User> list = new ArrayList<>();
User user1 = new User("zhangsan","123456","220302200301100248","13456315797");
list.add(user1);
Scanner sc = new Scanner(System.in);
loop: while (true) {
System.out.println("---------------欢迎来到学生管理系统升级版-------------");
System.out.println("请输入操作 1登录 2注册 3忘记密码 4退出");
int choose = sc.nextInt();
switch (choose) {
case LOGIN -> login(list);
case REGISTER -> register(list);
case FORGET_PASSWORD -> forgetPassword(list);
case EXIT ->{
System.out.println("谢谢使用,再见!");
System.exit(0);
}
default -> System.out.println("没有这个选项,操作失败!");
}
}
}
//注册功能
public static void register(ArrayList<User> list){
Scanner sc = new Scanner(System.in);
User user = new User();
//判断用户名是否合法
while(true){
System.out.println("请输入想要注册的用户名:");
String userName = sc.next();
int index = getIndex(list,userName);
int count = 0;
if(index >= 0){
System.out.println("当前用户名已存在,请重新输入!");
}else {
loop1:if (userName.length() >= 3 && userName.length() <= 15) {
for (int i = 0; i < userName.length(); i++) {
//System.out.println(userName.length());
char c = userName.charAt(i);
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9')) {
System.out.println("用户名需由字母和数字组成,请重新输入!");
break loop1;
} else {
if ( c >= 'A') {
count++;
}
}
}
//System.out.println(count);
if (count > 0 && count < userName.length()) {
System.out.println("用户名添加成功!");
user.setUserName(userName);
break;
}else {
System.out.println("用户名需由字母和数字组成,请重新输入!");
}
} else {
System.out.println("用户名长度须在3~15之间,请重新输入!");
}
}
}
//键盘录入两次密码,判断是否一致
while (true) {
System.out.println("请输入设置的密码:");
String password1 = sc.next();
if(password1.length() >=6) {//密码长度的设置是我自己加的,个人感觉这样更规范
System.out.println("请再次输入设置的密码:");
String password2 = sc.next();
if (password1.equals(password2)) {
System.out.println("密码设置成功!");
user.setPassword(password1);
break;
} else {
System.out.println("两次密码输入不一致,请重新输入!");
}
}else{
System.out.println("密码长度不小于6位,请重新输入!");
}
}
//验证身份证号
while(true){
int count = 0;
System.out.println("请输入身份证号:");
String idNumber = sc.next();
//System.out.println(idPassword.length());
if (idNumber.length() == 18){
char first = idNumber.charAt(0);
if (first == '0'){
System.out.println("身份证号码错误,请重新输入!");
}else {
for (int i = 0; i < idNumber.length() - 1; i++) {
char every = idNumber.charAt(i);
//System.out.println(every);
if (every < '0' || every > '9') {
System.out.println("身份证号码错误,请重新输入!");
break;
} else {
count++;
}
}
char end = idNumber.charAt(idNumber.length() - 1);
if (count == 17) {
if (end >= '0' && end <= '9' || end == 'x' || end == 'X') {
System.out.println("身份证号码添加成功!");
user.setIdNumber(idNumber);
break;
} else {
System.out.println("身份证号码错误,请重新输入!");
break;
}
}
}
}else{
System.out.println("身份证号码错误,请重新输入!");
}
}
//验证手机号
while(true){
int count = 0;
System.out.println("请输入您的手机号码:");
String phoneNumber = sc.next();
char nf = phoneNumber.charAt(0);
if (phoneNumber.length() != 11){
System.out.println("手机号码不正确,请重新输入!");
}else {
if (nf == '0') {
System.out.println("手机号码不正确,请重新输入!");
}else{
for (int i = 0; i < phoneNumber.length(); i++) {
//System.out.println(phoneNumber.charAt(i));
if (phoneNumber.charAt(i) < '0' || phoneNumber.charAt(i) > '9') {
System.out.println("手机号码不正确,请重新输入!");
break;
}else {
count++;
}
}
if(count == phoneNumber.length()) {
System.out.println("手机号码添加成功!");
user.setPhoneNumber(phoneNumber);
list.add(user);
break;
}
}
}
}
}
//判断用户名是否已存在
public static int getIndex(ArrayList<User> list, String str){
for (int i = 0; i < list.size(); i++) {
String have = list.get(i).getUserName();
if(have.equals(str)){
return i;
}
}
return -1;
}
//登录功能
public static void login(ArrayList<User> list){
Scanner sc = new Scanner(System.in);
// for (int i = 0; i < list.size(); i++) {
// System.out.println(list.get(i).getUserName());
// }
loop:while (true) {
System.out.println("请输入用户名:");
String userName = sc.next();
int index = getIndex(list,userName);
if(index >= 0){
System.out.println("请输入密码:");
String password = sc.next();
//输入验证码并进行判断
while (true) {
String verificationCode = getVerification();
System.out.println(verificationCode);
System.out.println("请输入验证码,不区分大小写:");
String str = sc.next();
if(str.equalsIgnoreCase(verificationCode)){
//System.out.println("验证码正确!");
break;
}else{
System.out.println("验证码错误,请重试!");
}
}
//判断用户名和密码
for (int i = 0; i < 3; i++) {
if (password.equals(list.get(index).getPassword())){
System.out.println("登录成功!");
break loop;
}else{
System.out.println("用户名或密码不正确,请重新输入!");
break;
}
}
}else{
System.out.println("该用户未注册,请注册成功后再登录!");
return;
}
}
}
//获取验证码
public static String getVerification(){
char[] str = new char[52];
for (int i = 0; i < str.length; i++) {
if(i<=25){
str[i] = (char)('a'+i);
}else{
str[i] = (char)('A'+i-26);
}
//System.out.println(c);
}
String result = "";
Random r = new Random();
for (int i = 0; i < 4; i++) {
int index = r.nextInt(52);
result = result + str[index];
}
int number = r.nextInt(10);
result = result + number;
char[] chs = result.toCharArray();
int index = r.nextInt(chs.length);
char temp = chs[index];
chs[index] = chs[chs.length-1];
chs[chs.length-1] = temp;
String newResult = new String(chs);
return newResult;
}
//忘记密码
public static void forgetPassword(ArrayList<User> list){
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String userName = sc.next();
int index = getIndex(list,userName);
if(index >= 0){
System.out.println("请输入身份证号:");
String idPassword = sc.next();
System.out.println("请输入手机号:");
String phoneNumber = sc.next();
if (list.get(index).getIdNumber().equals(idPassword) && list.get(index).getPhoneNumber().equals(phoneNumber)){
System.out.println("请输入密码:");
String password = sc.next();
list.get(index).setPassword(password);
System.out.println("修改成功!");
}else{
System.out.println("身份证号码或手机号码错误,删除失败!");
}
}else{
System.out.println("该用户名不存在,请注册后重试!");
}
}
}
3.权限修饰符
权限修饰符:是用来控制一个成员能够被访问的范围的。
可以修饰成员变量,方法,构造方法,内部类。
#权限修饰符的使用规则
实际开发中,一般只用private和public
●成员变量私有
●方法公开
特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有。
4.代码块
局部代码块:写在方法里面的单独的一对{}(已淘汰)
作用:提前结束变量的生命周期,节约内存
构造代码块:写在成员位置的代码块,优先于构造方法执行,可以把重复的代码写在代码块里面(不够灵活)
如果以后构造方法里面形参各不相同,又想在其中几个方法中实现相同代码,那么可以有以下两种解决办法
#####静态代码块(很重要)
格式:static{l}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
使用场景:在类加载的时候,做一些数据初始化的时候使用。