继承&方法重写&多态
1.继承
继承使用关键字extends,是java中实现代码重用的重要手段之一,将重复的代码抽取到父类中,明确程序结构。java中是单根继承的,一个子类只能有一个直接父类。间接父类没有上限;
package com.qfedu.test4;
/**
* 宠物父类
* 这里书写各个宠物共有的属性 和 方法
* @author WHD
*
*/
public class Pet {
protected String name;
private int health;
private int love;
protected String getName() {
return name;
}
protected 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(){
System.out.println("父类的无参构造方法");
}
public Pet(String name,int health,int love) {
this.name = name;
this.health = health;
this.love = love;
}
/**
* 用于打印狗狗信息的方法
*/
public void print() {
System.out.println("宠物的名字是" + this.getName()
+ "健康值是" + getHealth()
+ "爱心值是" + love);
}
}
package com.qfedu.test4;
/**
* 狗类
* 名字
* 健康值
* 爱心值
* 品种
* @author WHD
*
*/
public class Dog extends Pet{
private String strain;
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public Dog() {}
public Dog(String name,int health,int love,String strain) {
super(name, health, love); // 调用父类有参构造方法 this()
this.strain = strain;
}
}
package com.qfedu.test4;
/**
* 企鹅类
* 名字
* 健康值
* 爱心值
* 性别
* @author WHD
*
*/
public class Penguin extends Pet{
private String sex;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Penguin() {
}
public Penguin(String name,int health,int love,String sex) {
super(name, health, love);
this.sex = sex;
}
}
package com.qfedu.test4;
/**
* 问题1:创建子类对象 调用打印的方法 但是不能打印子类单独有的属性
* 子类可以继承父类的哪些内容?
* 默认修饰的属性和方法 父子类在同包中
* protected修饰的属性和方法
* public修饰的属性和方法
* private修饰的不能被继承
* 子类访问父类的构造方法
* 1.子类创建对象默认调用父类的无参构造方法
* 2.除非显式调用父类的有参构造方法
* 3.子类只能调用父类的一个构造方法 无参 有参 只能调一个
* super关键字 super表示父类对象
* 访问父类的
* 访问权限允许属性 super.属性名
* 访问权限允许方法 super.方法名
* 构造方法 只能出现在子类构造方法的第一句 super();
* @author WHD
*
*/
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("大黄");
dog.setHealth(100);
dog.setLove(70);
dog.setStrain("哈士奇");
dog.print();
System.out.println("品种是" + dog.getStrain());
Penguin penguin = new Penguin();
penguin.setName("小黑");
penguin.setHealth(80);
penguin.setLove(75);
penguin.setSex("雌");
penguin.print();
System.out.println("性别是" + penguin.getSex());
System.out.println("============================");
Dog dog1 = new Dog("小黄", 85, 85, "大金毛");
dog1.print();
}
}
问题一:创建子类对象 调用打印的方法 但是不能打印子类单独有的属性;子类可以继承父类的哪些内容?
1.默认修饰的属性和方法 父类在同包中
2.protected修饰的属性和方法
3.public修饰的属性和方法
4.private修饰的不能被继承
子类访问父类的构造方法:
1.子类创建对象默认调用父类的无参构造方法
2.除非显式调用父类的有参构造方法
3.子类只能调用父类的一个构造方法 无参 有参 只能调一个
super关键字 super表示父类对象
访问父类的
访问权限允许属性 super.属性名
访问权限允许方法 super.方法名
构造方法 只能出现子类构造方法的第一句 super();
2.super关键字
super关键字表示父类对象,与this关键字不同,this表示当前对象
2.1super关键字访问属性
使用super.父类属性名访问父类中访问权限允许的属性
比如:
1.使用默认修饰符修饰,父子类在同包中;
2.使用protect修饰的属性;
3.使用pubilc修饰的属性;
2.2 super关键字方法
使用super.父类方法名访问父类中访问权限允许的方法:
比如:
1.使用默认修饰符修饰的方法,父子类在同包中
2.使用protected修饰的方法
3.使用public修饰方法
2.3super关键字构造方法
使用super()访问父类的构造方法 必须在子类构造的第一句,与this规则相似
1.子类默认调用父类的无参构造方法,创建子类对象,先创建父类对象;
2.如果子类显示调用了父类的有参构造,那么将不再调用父类的无参
3.子类调用父类的构造方法,只能调用一个
2.方法重写
2.1使用方法重写改造宠物信息系统
package com.qfedu.test4;
/**
* 宠物父类
* 这里书写各个宠物共有的属性 和 方法
* @author WHD
*
*/
public class Pet {
String name;
int health;
int love;
// 封装的快捷键 alt + s + r
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;
}
void print() {
System.out.println("宠物的名字是" + this.getName()
+ "健康值是" + getHealth()
+ "爱心值是" + love);
}
public Pet() {
System.out.println("Pet父类的无参构造被执行了");
}
public Pet(String name,int health,int love) {
this.name = name;
this.health = health;
this.love = love;
}
public Pet m1() {
return null;
}
}
package com.qfedu.test4;
/**
* 狗类
* 名字
* 健康值
* 爱心值
* 品种
*
* @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("狗狗的品种是" + this.getStrain());
}
public Dog m1() {
return null;
}
public void printDogName() {
System.out.println("狗狗的名字是" + super.name);
}
public Dog(String name,int health,int love,String strain) {
super(name, health, love);
this.strain = strain;
}
public Dog() {
}
}
package com.qfedu.test4;
/**
* 企鹅类
* 名字
* 健康值
* 爱心值
* 性别
* @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 print() {
super.print();
System.out.println("企鹅的性别是" + getSex());
}
public Penguin(String name,int health,int love,String sex ) {
super(name, health, love);
this.sex = sex;
}
public Penguin() {
}
}
2.2方法重写的特点
方法重写:
子类重写父类的方法: 必须满足以下几个规则 同时 如果子类重写了父类的方法那么通过子类对象只能调用重写以后的方法
1.存在于父子类之间;
2.方法名称相同,参数列表相同;
3.返回值相同,或者是其子类;
4.访问修饰符不能严于父类 不能窄化访问特权;
5.不能抛出比类更多的异常;
6.父类的非静态方法 不能被重写为静态方法;
7.父类的静态方法 可以被继承 但是不能被重写
2.3方法重写和方法重载的区别
此处有图片
2.4Object类
Object类是所有类的超类,父类,我们自定义的类将默认继承此类;
Object类中提供了一些常用的方法,比如toString,equals等
此处有图片
2.4.1 toString方法
toString方法
toString方法属于于Object类提供的方法,用于打印对象的包名+类名+@+十六进制的hashCode的hash值
我们可以重写toString来打印对象的属性名和值
package com.qfedu.test6;
/**
* toString方法
* toString方法属于Object类提供的方法,用于打印对象的包名+类名+@+hashCode hash值
* 我们可以重写toString来打印对象的属性名和值
* @author WHD
*
*/
public class Student {
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;
}
// @Override
// public String toString() {
// return "Student[属性名name:" + name + ",属性名idCard:" + idCard + "]";
// }
// alt + s
@Override
public String toString() {
return "Student [name=" + name + ", idCard=" + idCard + "]";
}
public static void main(String[] args) {
Student stu1 = new Student();
stu1.setName("赵四");
stu1.setIdCard("2345487851210");
// 直接调用对象的toString方法 返回的是当前对象的 包名 + 类名 @ 十六进制的 hash值 地址值
System.out.println(stu1.toString());
// 我们直接输出一个对象 相当于调用 当前对象的toString方法
System.out.println(stu1);
}
}
2.42 equals方法
equals方法是Object提供的用于比较引用数据类型地址的方法,我们可以根据自己的需求重写这个方法,比如String类对这个 方法进行了重写,将会原来的比较地址重写为了比较内容;
面试题:
==和equals的区别?
==比较基本数据类型,比较值,比较引用数据类型,比较的是地址
equals本身也比较地址,但是我们重写按照我们自己的规则来比较\
package com.qfedu.test7;
/**
* equals 比较两个对象的地址是否相同
* == 和 equals的区别?
* == 在比较基本数据类型 比较的是值 比较引用数据类型 比较的是内存中的地址
* equals本身这个方法属于Object类中定义的 本身也是比较地址 String使用equals方法比较的是内容
* 因为String类对equals方法进行了重写 重写为比较内容
* abcdef abcdef
* 假设生活中有"两个人" 名字和身份证都是一样的 这"两个人"就是一个人
* 也就是说如果两个人的信息都一样 使用equals比较应该为true
* @author WHD
*
*/
public class Student {
public static void main(String[] args) {
int a = 20;
int b = 20;
System.out.println(a == b);
Student stu1 = new Student();
Student stu2 = new Student();
System.out.println(stu1 == stu2);
// System.out.println(stu1.equals(stu2));
System.out.println("========================");
System.out.println(myEquals("abcdef", "abcde"));
String str1 = "abcd";
String str2 = "abcd";
System.out.println(str1.equals(str2));
System.out.println("========================");
Student stu3 = new Student("赵四", "45124878123245561121");
Student stu4 = new Student("赵四", "45124878123245561121");
System.out.println(stu3.equals(stu4));
System.out.println(stu3 == stu4);
}
/**
* 按照我们自己定义的规则来比较两个Student对象
*/
public boolean equals(Object obj) {
if(this == obj) { // 如果两个对象地址相同 直接返回true 后续代码不再执行
return true;
}
// 接下来我们要比较当前对象和传入对象的名字 以及 身份证号
Student stu = (Student) obj; // 先强转为Student类型 如果不强转 将无法获取对应的信息
if(this.getName().equals(stu.getName()) && this.getIdCard().equals(stu.getIdCard())) {
return true;
}
return false;
}
/**
* 模拟实现String类的equlas方法
*/
public static boolean myEquals(String str1,String str2) {
char [] ch1 = str1.toCharArray();
char[] ch2 = str2.toCharArray();
if(str1.length() != str2.length()) {
return false;
}
for(int i = 0;i < ch1.length;i++) {
if(ch1[i] != ch2[i]) {
return false;
}
}
return true;
}
public Student() {
}
public Student(String name,String idCard) {
this.name = name;
this.idCard = idCard;
}
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;
}
}
3.对象和方法
3.1 对象
与数组作为形参一样,也属于引用数据类型,传递的依然是地址。写法和基本数据类型一样,形参同样规定参数的个数,类型,顺序。
class Person{
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 Person(String name,int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "Person[name:" + name + ",age:" + age + "]";
}
public Person() {
}
public static boolean checkAge(Person person) {
if(person.getAge() >= 18) {
return true;
}
return false;
}
public static void main(String[] args) {
// 这里先创建Person类型对象
Person p = new Person("赵四", 17);
// 作为参数传入checkAge方法中
System.out.println(checkAge(p));
}
}
3.2 对象作为返回值
与基本数据类型的返回值一样,return以后的内容必须和返回值类型保持一致
class Person{
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 Person(String name,int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "Person[name:" + name + ",age:" + age + "]";
}
public Person() {
}
public static Person createPerson(){
Scanner input = new Scanner(System.in);
System.out.println("请输入名字");
String name = input.next();
System.out.println("请输入一个年龄");
int age = input.nextInt();
Person p = new Person(name, age);
return p;
}
public static void main(String[] args) {
System.out.println(createPerson()); // 调用createPerson方法 将得到一个对象
// 直接打印 表示调用当前对象的toString方法 此时我们已经重写toString方法 将直接打印属性名和值
}
}
3.3类类型的属性
在很多的情况下,我们使用基本数据类型或者String类型无法描述当前的属性,所以我们必须使用自己定义的类型(类类型)来定义属性。
比如:学生类的班级信息,班级我们也可以分析为一个类,此类中有关于班级的所有的属性(信息)
package com.qfedu.test7;
/**
* 学生类中的属性
名字 : name - String
身份证号: idCard - String
班级: grade - Grade
* @author WHD
*
*/
public class Student {
private String name; // 名字
private String idCard; // 身份证
private Grade grade; // 班级
// 封装
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
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 Student() {
}
// 全参构造
public Student(String name,String idCard) {
this.name = name;
this.idCard = idCard;
}
}
class Grade{
private String gradeName;
private int seatCount;
public int getSeatCount() {
return seatCount;
}
public void setSeatCount(int seatCount) {
this.seatCount = seatCount;
}
public String getGradeName() {
return gradeName;
}
public void setGradeName(String gradeName) {
this.gradeName = gradeName;
}
}
class Test{
public static void main(String[] args) {
Student stu = new Student();
stu.setName("赵四");
stu.setIdCard("45121578412311421");
Grade grade = new Grade();
grade.setGradeName("三年二班");
grade.setSeatCount(66);
stu.setGrade(grade);// 此时给grade属性设置值的方法 需要的参数的是Grade类型的 所有我们必须先newgrade
// 将学生信息打印
System.out.println(stu.getName());
System.out.println(stu.getIdCard());
// 打印班级信息 因为班级属于对象类型的 直接打印将调用对象的toString方法 打印包名类名 + 地址值
// 所以我们先通过getGrade()方法获取到对象,再继续+点调用 getGradeName()和getSeatCount()方法
System.out.println(stu.getGrade().getGradeName()); // 班级名称
System.out.println(stu.getGrade().getSeatCount()); // 座位数
}
}