方法重写和多态
1.方法重写
规则:
1.存在于父子类之间
2.方法名称相同
3.参数列表相同
4.返回值相同或者是其子类
5.访问权限不能严于父类,不能窄化访问权限
6.不能抛出比父类更多的异常
以下为扩展:
7.父类的静态方法,可以继承,但是不能重写
(即使与父类写的一模一样,也可以正常运行,但通过@Override可知此时不是重写)
8.父类的非静态方法,不能重写为静态方法
9.父类的静态方法,子类不能定义同名的非静态方法(也就如7所说,定义了一模一样的静态可以运行)
@添加JDK源文件
当查看某个jdk写好的源代码却发现找不到时,配置如下:
2.Object类
概念:
Object类是所有类的超类,父类,我们自定义的类将默认继承此类,此类提供了一些常用的方法
toString(): 返回此对象的包名+类名+@+十六进制的hash值
equals(): 内部使用==比较两个对象的地址是否相同
getClass(): 返回当前对象的类的类型 class+全限定类名,一般与getName 连用,获取全限定类名。
h连用,获取shCode():返回更具当前对象堆地址所运算出来的hash值,是地址的一种表现形式,但不是地址。
2.1重写toString
我们直接输出一个对象,将默认调用此对象的toString方法
作用:在日常开发中,我们直接输出一个对象,我们所需要的效果是打印对象的属性名称和属性值,所以我们需要对toString方法进行重写
package com.qfedu.test3;
public class Student {
private String name;
private int 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;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
public static void main(String[] args) {
Student stu = new Student();
System.out.println(stu);
}
}
2.2重写equals
面试题:==和equals的区别
答案:1.==比较基本数据类型时,比较的是值,但比较引用数据类 型时,比较的是内存中的地址,
2.equals方法本身也比较地址(因为我们查看源代码会发现,equals内部依然调用==比较),String类之所以可以比较字符串的内容,因为String对equals方法进行了重写,重写为比较字符串的内容,我们在实际开发中也会重写equals方法,按照我们自己的规则比较两个对象,比如当"两个人"的身份证号名字相同的情况,那么使用equals比较应该输出为true
模拟String类编写equals方法
package com.qfedu.test4;
public class MyStringEquals {
public static boolean myEquals(String str1,String str2) {
char [] ch1 = str1.toCharArray();
char [] ch2 = str2.toCharArray();
if(ch1.length != ch2.length) {
return false;
}
for (int i = 0; i < ch1.length; i++) {
if(ch1[i] != ch2[i]) {
return false;
}
}
return true;
}
public static void main(String[] args) {
System.out.println(myEquals("abcd", "abc"));
}
}
编写equals实现按照名字和身份证号比较
package com.qfedu.test5;
public class Person {
private String name; // 名字
private String idCard; // 身份证号
public Person() {
}
public Person(String name, String idCard) {
this.name = name;
this.idCard = idCard;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
// 表示判断obj是否属于Person类的实例 是的话 返回true 否则false
if(obj instanceof Person) {
Person person = (Person) obj;
if(this.getName().equals(person.getName()) && this.getIdCard().equals(person.getIdCard())) {
return true;
}
}else {
System.out.println("不是人类,只能人跟人比");
}
return false;
}
public static void main(String[] args) {
Person p1 = new Person("赵四", "41023564578");
Penguin penguin = new Penguin("小黑", "雌");
System.out.println(p1.equals(penguin));
String str1 = new String("abc");
System.out.println(str1.equals(penguin));
int a = 20;
m1(a);
}
public static void m1(double a) {
}
}
2.3重写hashCode
hashCode是由杂凑算法根据地址算出来的一个数值,可以保证每个对象产生不一样的数值
杂凑算法特点:正向是快速的,但是是不可逆的(有哈希冲突的可能)
在一些散列数据结构中,比如HashSet、HashMap、Hashtable等,要求如果两个对象使用equals比较输出为true,那么hash值必须一样,所以我们要求重写equals必须重写hashCode方法
两个hashCode相同的对象 不一定是同一个对象 使用equals不一定为true
package com.qfedu.test5;
public class Person {
private String name; // 名字
private String idCard; // 身份证号
public Person() {
}
public Person(String name, String idCard) {
this.name = name;
this.idCard = idCard;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
// 表示判断obj是否属于Person类的实例 是的话 返回true 否则false
if(obj instanceof Person) {
Person person = (Person) obj;
if(this.getName().equals(person.getName()) && this.getIdCard().equals(person.getIdCard())) {
return true;
}
}else {
System.out.println("不是人类,只能人跟人比");
}
return false;
}
public int hashCode() {
// 这里要求统一使用31作为权重 31作为计算hash值的重要条件
// 31是一个特殊的质数 任何数乘以31 等同于 这个数 左移5位 减去这个数本身
// 31作为权重可以保证我们产生的hash值 均匀分布在当前的 int取值范围内
// 不会过大 也不会过小
int prime = 31;
int result = 1;
result = prime * result + (this.getName() == null ? 0 : this.getName().hashCode());
result = prime * result + (this.getIdCard() == null ? 0 : this.getIdCard().hashCode());
return result;
}
public static void main(String[] args) {
Person p1 = new Person("赵四", "41023564578");
Person p2 = new Person("赵四", "41023564578");
System.out.println(p1.equals(p2));
System.out.println(p1.hashCode() + "=======" + p2.hashCode());
// hashCode 方法 根据对地址计算出来的一个数值 这个数值是唯一的 重复的可能性 极小 可以忽略不计
// hash算法 杂凑算法 比如 我们按照一种新的方式来决定大家的座位
// 姓氏的笔画 决定 第几排
// 名字的笔画数除以一个数 可以得到列数
// 这种方式正向是快速的 不可逆的 王 丰
// 在一些散列的数据结构HashMap Hashtable HashSet 有这样一个规范 两个对象如果equals比较为true
// 那么它们的hashCode必须一样
// 两个hashCode相同的对象 不一定是同一个对象 使用equals不一定为true
System.out.println(2 * 31);
System.out.println((2 << 5) - 2);
}
}
2.4 getClass方法
getClass()方法用于获取一个Class类型的对象泛型实例,目前我们使用本方法直接调用getName()获取到一个对象的包名+类名
getClass().getName();
package com.qfedu.test1;
public class Test1 {
public static void main(String[] args) {
// getClass 方法返回类类型的泛型
// 目前我们在getClass之后继续调用getName方法 获取当前对象的包名 + 类名
Test1 test1 = new Test1();
System.out.println(test1.getClass().getName());
}
}
3.多态
多态:
1.多种形态,指同一动作,因为环境的不同,产生不同的效果
2.同一段代码,因为参数的不同,产生不同的结果
多态的实现方式:
1.父类作为形参 (子类作为实参,向上转型,自动实现,父类引用指向子类对象)
2.父类作为返回值
向上转型:
父类引用指向子类对象 Pet pet = new Dog();
向上转型可以调用父类访问权限允许的方法和子类重写父类的方法(子类的),不能调用子类单独的方法。
向下转型:
将指向子类对象的父类引用强制转换为子类对象 Dog dog = (Dog)pet;
可以调用子类独有的方法以及父类访问权限允许的方法和子类重写父类的方法(子类的)
instanceof关键字
a instanceof A 表示判断a是否属于A的实例,返回值为布尔类型,用于避免强转出现的类型转换异常ClassCastException
package com.qfedu.test2;
/**
* 主人类 用于提供操作宠物的方法
* 多态实现方式
* 1.父类作为形参 比如我们写看病和游泳的方法 我们之前重写Object类中equals方法
* 2.父类作为返回值
* @author WHD
*
*/
public class Master {
/**
* 当前方法给狗狗看病
* @param dog
// */
// public void cureDog(Dog dog) {
// if(dog.getHealth() < 60) {
// dog.toHospital();
// }else {
// System.out.println("很健康,不需要看病");
// }
// }
//
//
//
// public void curePenguin(Penguin penguin) {
// if(penguin.getHealth() < 80) {
// penguin.toHospital();
// }else {
// System.out.println("很健康,不需要看病");
// }
// }
//
//
//
// public void cureCat(Cat cat) {
// if(cat.getHealth() < 60) {
// cat.toHospital();
// }else {
// System.out.println("很健康,不需要看病");
// }
// }
// 以上我们书写的方法 可以实现给宠物看病 但是这样写弊端很多
// 1.如果随着家庭条件的富裕 养的宠物越来越多 发现需要重复的添加给宠物看病的方法
// 不符合 开闭原则 属于软件设计原则之一
// 1.开 对扩展开放
// 2.闭 对修改源代码关闭
// 为了解决以上问题 我们需要编写一个方法 用于解决给所有宠物看病的功能
// 宠物店举行活动 抽奖 送宠物
// 一等奖 送企鹅一只
// 二等奖 送狗一只
// 三等奖 送猫咪
// 以上宠物 均为雌性
/**
* 第一名
* @return 返回一个企鹅
*/
public Penguin No1(){
return new Penguin("大白", 100, 100, "雌");
}
/**
* 第二名方法
* @return
*/
public Dog No2() {
return new Dog("大黄", 100, 100, "拉布拉多");
}
/**
* 第三名方法
* @return
*/
public Cat No3() {
return new Cat("花花", 100, 100, "黄色");
}
public Pet givePet(String str) {
if(str.equals("一等奖")) {
return new Penguin("大白", 100, 100, "雌");
}else if (str.equals("二等奖")) {
return new Dog("大黄", 100, 100, "拉布拉多");
}else if(str.equals("三等奖")) {
return new Cat("花花", 100, 100, "黄色");
}else {
System.out.println("谢谢惠顾");
return null;
}
}
public double getNum() {
int a = 20;
return a;
}
public void curePet(Pet pet) {
pet.toHospital();
}
/**
* 父类作为形参 子类作为实参 向上转型 自动实现 父类引用指向子类对象
* 是实现多态的方式之一
* @param pet
*/
public void showerPet(Pet pet) {
pet.shower();
}
public static void main(String[] args) {
Dog dog = new Dog(55, 30, "大黄", "大金毛");
Master zhaosi = new Master();
// zhaosi.cureDog(dog);
// System.out.println("狗狗看完病了" + dog.getHealth());
zhaosi.curePet(dog);
System.out.println("狗狗看完病了" + dog.getHealth());
System.out.println("==================================");
Penguin penguin = new Penguin(25, 20, "雌", "大白");
zhaosi.curePet(penguin);
System.out.println("企鹅看完病了" + penguin.getHealth());
System.out.println("==================================");
Cat cat = new Cat("大花花", 65, 70, "绿色");
zhaosi.showerPet(cat);
System.out.println(cat.getHealth());
// 父类引用指向子类对象 此时此引用能够调用的方法是
// 子类重写父类的方法 和 父类访问权限允许的方法
// 不能调用子类单独的方法
Pet pet1 = new Dog();
pet1.setHealth(100);
pet1.setLove(20);
pet1.setName("大黑");
// 我们通过这种方式反而调用的方法会减少 为什么还使用方式呢?
Dog dog1 = new Dog();
dog1.setHealth(20);
// 我们可以通过子类new子类 但是多态的实现是当我们需要传入参数的时候实现的
Pet pet2 = new Cat();
Pet pet3 = new Penguin();
}
}
package com.qfedu.test2;
/**
* 多态 多种形态
* 指同一个动作 在不同的环境中 产生不一样的效果
* 指同一段代码 因为参数的不同 产生不一样的运行结果
* 回顾我们之前重写的equals方法 父类的形参是什么类型的?
* 是Object类型的
* 为什么是Object类型的? 咋不使用String类型呢 ?
* @author WHD
*
*/
public class Test1 {
public static void main(String[] args) {
// 父类作为形参 子类作为实参 向上转型 多态实现方式之一
// 此时可以调用 子类重写父类的方法 父类访问权限允许的方法
// 不能调用子类独有的方法
Pet dog = new Dog(100, 85, "大黄", "金毛");
// 狗类中单独的玩飞盘的方法 不能调用 此时需要向下转型 强转
// 指向子类对象的父类引用 需要转换为 子类类型
Dog dahuang = (Dog) dog; // 向下转型 强制转换
dahuang.playFlyDisc();
System.out.println("===========================");
Pet penguin = new Penguin(78, 85, "雌", "大白");
Penguin dabai = (Penguin) penguin;// 向下转型 强制转换
dabai.playIce();
System.out.println("===========================");
Pet pet = new Pet();
// pet 实例属于 Dog
if(pet instanceof Dog) {
Dog dog1 = (Dog) pet;
}
Master zhaosi = new Master();
// Pet pet = new Penguin(……);
// 父类引用指向子类对象
// 可以调用父类访问权限允许的方法 和 子类重写父类的方法
Pet pet1 = zhaosi.givePet("一等奖");
// 洗澡的方法是子类重写父类的
pet1.shower();
// pet.playIce(); 不能调用子类单独的方法
Pet pet2 = zhaosi.givePet("二等奖");
pet2.shower();
}
}
每日问题1:
1.instanceof关键字的作用,比如 a intstanceof A
2.子类可以继承父类的哪些内容?
3.分别说明方法重载和方法重写的特点
4.类和对象的关系
5.三目运算符的写法
6.switch支持的数据类型
7.包的命名规范是什么?
8.类的访问修饰符有哪些,分别代表什么意义?
9.类的成员访问修饰符有哪些,分别代表什么意义?
10.8种基本数据类型有哪些?
11.Object类中toString方法本身的作用是什么,我们为什么要重写
12.Object类中equals方法本身的作用是什么,跟String类中的equals有什么区别?
13.为什么重写equals必须要重写hashCode
每日问题2:
1.继承中实现多态的主要方式是什么?两种
2.如果引用数据类型强转错误,将会出现什么异常
3.现有子类Cake,父类Food,分别书写向上转型和向下转型并标明
4.向上转型可以调用哪些方法?
5.向下转型可以调用哪些方法?
6.==和equals的区别
7.描述构造方法重载
8.static关键字可以修饰哪些内容,分别有什么特点
解答1:
1.instanceof关键字的作用,比如 a intstanceof A
判断a是否属于A类的实例(对象)
2.子类可以继承父类的哪些内容?
public修饰的属性和方法 默认修饰的属性和方法,要求父子类在同包 protected修饰的属性和方法
3.分别说明方法重载和方法重写的特点
方法重载:同类中
方法名相同
参数列表不同
与返回值访问修饰符无关
方法重写:父子类中
方法名相同
参数列表相同
返回值相同或者是其子类
不能跑出比父类更多的异常
访问权限不能严于父类
静态方法可以被继承,但是不能被重写
非静态方法不能重写为静态方法
4.类和对象的关系
类是对象的抽象,对象是类的具体
5.三目运算符的写法
布尔值 ? 变量1 : 变量2
6.switch支持的数据类型
byte short int char String(JDK7+) 枚举
7.包的命名规范是什么?
域名倒置,全部小写,不能以点开头或者结尾
8.类的访问修饰符有哪些,分别代表什么意义?
public本项目中都可以访问,默认不写本包中可以访问
9.类的成员访问修饰符有哪些,分别代表什么意义?
public 项目范围
默认不写 本包中
protected 本包 子类中
private 本类中
10.8中基本数据类型有哪些?
byte short int long float double boolean char
11.Object类中toString方法本身的作用是什么,我们为什么要重写
包名+类名 @ 16进制hash值 需要打印对象的属性和属性值
12.Object类中equals方法本身的作用是什么,跟String类中的equals有什么区别?
equals本身也比较地址 String类是比较内容
13.为什么重写equals必须要重写hashCode
因为在一些数据结构中,equals比较相同的两个对象,hash值必须一样
解答2:
1.继承中实现多态的主要方式是什么?两种
父类作为返回值
父类作为形参
2.如果引用数据类型强转错误,将会出现什么异常
ClassCastException
3.现有子类Cake,父类Food,分别书写向上转型和向下转型并标明
Food food = new Cake();向上
Cake cake = (Cake)food;向下
4.向上转型可以调用哪些方法?
向上可以调用父类访问权限允许的方法,和子类重写父类的方法
5.向下转型可以调用哪些方法?
可以调用子类独有的方法以及题所述方法
6.==和equals的区别
==比较基本数据类型,比较的是值
==比较引用数据类型,比较的是地址
equals本身也比较地址,String类对其进行了重写,重写为比较字符串的内容
7.描述构造方法重载
因为构造方法都是类名,并且没有返回值,所以只需要参数列表不同即可,参数列表不 同又包括参数的个数,类型,顺序 8.static关键字可以修饰哪些内容,分别有什么特点
属性,静态属性,内存中只有一份拷贝,被类的所有对象共享,可以实现共享数据
方法,静态方法,本类中直接访问,其他类通过类名调用
代码块,静态代码块,在类加载的时候执行,多个静态代码块按照顺序执行,并且只执 行一次,用于初始化数据或者做一些前置操作