继承和方法重写
1.继承
继承是Java中实现代码重用的重要手段之一。Java中只支持单根继承,即一个类只能有一个直接父类。
子类与父类是 is-a 的关系,子类是父类
父子类信息编写原则:
父类中编写共有的属性和行为
子类中编写独有的属性和行为
public class Pet {
private String name;
private int health;
private int love;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public int getLove() {
return love;
}
public void setLove(int love) {
this.love = love;
}
public void print() {
System.out.println("宠物的名字是" + name + ",健康值是" + health + ",爱心值是" + love);
}
}
public class Dog extends Pet{
private String strain;
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public Dog() {}
}
public class Penguin extends Pet{
private String sex;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Penguin() {}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("大黄");// Pet继承而来的
dog.setHealth(100);// Pet继承而来的
dog.setLove(100); // Pet继承而来的
dog.setStrain("哈士奇"); // Dog类编写的
dog.print();
// 调用从父类继承而来的print方法打印信息 发现以下两个问题:
// 1.打印信息身份不准确
// 2.打印信息不具体
System.out.println("===============================================");
Penguin p1 = new Penguin();
p1.setName("大白");// Pet继承而来的
p1.setLove(100);// Pet继承而来的
p1.setHealth(100);// Pet继承而来的
p1.setSex("雌"); // Penguin类编写的
System.out.println(p1.getName());
System.out.println(p1.getHealth());
System.out.println(p1.getLove());
System.out.println(p1.getSex());
p1.print();
}
}
2. super关键字
super关键字指父类对象,可以访问父类权限允许的方法、属性、构造方法。
public class Pet {
protected String name;
protected int health;
protected int love;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public int getLove() {
return love;
}
public void setLove(int love) {
this.love = love;
}
public void print() {
System.out.println("宠物的名字是" + name + ",健康值是" + health + ",爱心值是" + love);
}
}
public class Dog extends Pet{
private String strain;
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public Dog() {}
public void printDog() {
super.print();
System.out.println("狗狗的品种是" + strain);
}
public Dog(String name,int health,int love,String strain) {
this.strain = strain;
super.name = name;
super.health = health;
super.love = love;
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("大黄");
dog.setHealth(100);
dog.setLove(100);
dog.setStrain("金毛");
// dog.print();
dog.printDog();
System.out.println("===========================");
Penguin p1 = new Penguin();
p1.setName("大白");
p1.setLove(100);
p1.setHealth(100);
p1.setSex("雌");
p1.printPenguin();
System.out.println("===========================");
Dog dog1 = new Dog("小黑", 100, 100, "金毛");
dog1.printDog();
}
}
super关键字使用构造方法创建子类对象时,默认调用父类的无参构造方法,除非子类显式的调用父类的有参构造方法
子类必须调用父类的构造方法,无参或者有参必须调用一个
public class Pet {
protected String name;
protected int health;
protected int love;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public int getLove() {
return love;
}
public void setLove(int love) {
this.love = love;
}
public Pet(String name,int health,int love) {
this.name = name;
this.health = health;
this.love = love;
}
/**
* 父类中推荐都提供无参 方便子类使用
*/
public Pet() {}
public void print() {
System.out.println("宠物的名字是" + name + ",健康值是" + health + ",爱心值是" + love);
}
}
/**
* 狗类
* 品种
* 打印狗狗信息
* @author WHD
*
*/
public class Dog extends Pet{
private String strain;
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public void printDog() {
print();
System.out.println("狗狗的品种是" + strain);
}
/**
* 因为父类的无参构造被覆盖了 所以Dog子类报错
* 解决方案:编写有参构造调用父类的有参 不再调用无参
* @param name
* @param health
* @param love
* @param strain
*/
public Dog(String name,int health,int love,String strain) {
super(name, health, love);
this.strain = strain;
}
}
/**
* 企鹅类
* 性别
* 打印信息
* @author WHD
*
*/
public class Penguin extends Pet{
private String sex;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void printPenguin() {
super.print(); // 继承自父类的方法 可以使用super加点 或者this加点 或者直接书写 方法名
System.out.println("企鹅的性别是" + sex);
}
}
3.方法重写
方法重写的要求:
1.父子类之间的
2.方法名称相同
3.参数列表相同
4.访问权限不能严于父类,不能窄化父类的访问权限
5.返回值相同或者是其子类
6.父类的静态方法可以被继承,但是不能被重写,非静态方法不能重写为静态方法
7.不能抛出比父类更多的异常
@Override注解:用于子类的方法上,表示此方法为重写父类的方法,如果没有符合以上重写的规则,那么将编译报错。
/**
* 狗类
* 品种
* 打印狗狗信息
* @author WHD
*
*/
public class Dog extends Pet{
private String strain;
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
/**
* 方法重写
* 1.父子类之间的
* 2.方法名称相同
* 3.参数列表相同
* 4.访问权限不能严于父类 不能窄化父类的访问权限
* 5.返回值相同 或者是其子类
*
* 6.父类的静态方法可以被继承 但是不能被重写 非静态方法不能重写为静态方法
* 7.不能抛出比父类更多的异常
*/
@Override
public void print() {
super.print();
System.out.println("狗狗的品种是" + strain);
}
public static void m1() {
}
public Dog(String name,int health,int love,String strain) {
super(name, health, love);
this.strain = strain;
}
}
4. Object类
Object类是所有类的父类,所有的类将默认继承自此类
此类中提供了一些常用的方法,实际开发中我们经常重写这些方法
4.1 重写toString方法
我们直接打印一个对象将默认调用此对象的toString方法,返回值为包名类名+@+哈希值
如果我们不想要这个效果,可以重写toString方法
/**
* 学生类
* 名字
* 年龄
* @author WHD
*
*/
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;
}
public String toString() {
String str = super.toString();
return "Student[name=" + name + ",age=" + age + "]" + str;
}
public static void main(String[] args) {
Student stu = new Student();
stu.setName("赵四");
stu.setAge(17);
System.out.println(stu.getName());
System.out.println(stu.getAge());
System.out.println(stu); // 直接打印一个对象将调用从父类继承而来的toString方法
System.out.println(stu.toString());
}
}
4.2重写equals方法
==和equals的区别?(面试题)
比较基本数据类型时比较的是值,比较引用数据类型时比较的是内存中的地址
equals只能比较引用数据类型,本身也使用比较两个对象在内存中的地址
String类对equals方法进行了重写,改为了比较内容
public class Person {
private String name;
private String 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 Person() {
}
public Person(String name, String idCard) {
this.name = name;
this.idCard = idCard;
}
/**
* 重写equals方法
* 按照名字和身份证号来比较 如果两个对象的名字和身份证号都相同
* 那么应该返回为true
*/
public boolean equals(Object obj) {
// 第一步 先比较地址 如果地址相同 则直接return true 不需要继续比较 了
if(this == obj) {
return true;
}
// 代码执行到这里 表示 地址不同 那么我们应该比较内容 名字和身份证号
// 因为obj是父类对象 而父类对象不能直接获取name属性的值 所以需要强制向下转换
Person p1 = (Person) obj;
if(this.getName().equals(p1.getName()) && this.getIdCard().equals(p1.getIdCard())) {
return true;
}
// 如果代码能够执行到这里 表示 上述条件不成立
return false;
}
}
public class TestPerson {
public static void main(String[] args) {
// 如果有“两个人”身份证和名字信息都一致 那么肯定是同一个人 也就是说 使用equals比较应该为true
Person p1 = new Person("赵四", "45783315782121324561");
Person p2 = new Person("赵四", "45783315782121324561");
System.out.println(p1.equals(p2));
}
}
4.3重写hashCode方法
为什么要重写hashCode方法?
因为在一些散列数据结构中,如果两个对象使用equals比较为true,那么通常hashCode也要相同
hashCode是根据地址等一些信息计算出来的一个int类型的数值
杂凑算法特点:正向是快速的,不可逆的
public class Person {
private String name;
private String 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 Person() {
}
public Person(String name, String idCard) {
this.name = name;
this.idCard = idCard;
}
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
Person p1 = (Person) obj;
if(this.getName().equals(p1.getName()) && this.getIdCard().equals(p1.getIdCard())) {
return true;
}
return false;
}
public int hashCode() {
// 权重 为31 表示计算hashCode的决定性因素
int prime = 31;
int result = 1;
result = result * prime + (this.getName() == null ? 0 : this.getName().hashCode() );
result = result * prime + (this.getIdCard() == null ? 0 : this.getIdCard().hashCode());
return result;
}
}
4.4使用getClass方法
getClass() 获取当前类类型的对象
getName()方法获取当前对象所属类的包名+类名
public class TestPerson {
public static void main(String[] args) {
Person p1 = new Person("赵四", "1242323565789");
System.out.println(p1.getClass().getName());
System.out.println(p1.toString());
}
}