Java基础学习 ---封装和继承
static关键字&方法重写&多态
1.static关键字
1.1 static修饰属性
static修饰属性,表示此属性属于整个类,不属于任何对象,可以被此类的所有对象共享,在内存中
只存在一份。推荐使用类名加点访问,也可以(不推荐)通过对象名加点访问。
对象创建的过程:
1.将类信息文件加载到方法区(加载类的操作),加载类之前会先判断此类是否被加载过
如果已经被加载过,则直接进行第二步
如果没有被加载过,则先加载类
此时静态的信息会在方法区完成初始化,即有空间,有值
2.在堆中开辟空间,此时实例级别的属性将有默认值
3.将堆中的地址赋值给栈中的引用
| 描述 | 静态属性 | 实例属性 |
|---|---|---|
| 初始化时机: | 静态的随着类的加载而初始化 | 实例的随着对象的创建而初始化 |
| 存储位置: | 静态的存储在方法区 | 实例的存储在堆中(对象中) |
| 存储性质: | 静态的随着类加载而存在,类只加载一次,所以静态的属性只存在一份 | 实例的随着对象的创建而存在,每创建一次对象就存在一份 |
/**
* @author WHD
* @description TODO
* @date 2024/1/9 10:24
* 学生类
* 使用代码模拟实现多个学生共享饮水机的操作
*
* static修饰属性,表示此属性属于整个类,不属于任何对象,可以被此类的所有对象共享,在内存中
* 只存在一份。推荐使用类名加点访问,也可以(不推荐)通过对象名加点访问。
*
* 对象创建的过程:
* 1.将类信息文件加载到方法区(加载类的操作),加载类之前会先判断此类是否被加载过
* 如果已经被加载过,则直接进行第二步
* 如果没有被加载过,则先加载类
* 此时静态的信息会在方法区完成初始化,即有空间,有值
* 2.在堆中开辟空间,此时实例级别的属性将有默认值
* 3.将堆中的地址赋值给栈中的引用
*
* 实例属性和静态属性的区别?
* 初始化时机:静态的随着类的加载而初始化 实例的随着对象的创建而初始化
* 存储位置:静态的存储在方法区 实例的存储在堆中(对象中)
* 存储性质:
* 静态的随着类加载而存在,类只加载一次,所以静态的属性只存在一份
* 实例的随着对象的创建而存在,每创建一次对象就存在一份
*
*
*/
public class Student {
String name;
static int capacity = 100; // 100升
public void getWater(){
if(capacity > 0){
capacity -= 2;
System.out.println(name + "接水2L,还剩余:" + capacity + "L");
}else{
System.out.println("没水了");
}
}
public static void main(String[] args) {
Student stu1 = new Student();
stu1.name = "赵四";
stu1.getWater();
stu1 = null;
Student stu2 = new Student();
stu2.name = "广坤";
stu2.getWater();
Student stu3 = new Student();
stu3.name = "小宝";
stu3.getWater();
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/9 10:35
* 人类
*/
public class Person {
String name;
static String country = "中华人民共和国";
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "赵四";
p1.country = "中国";
System.out.println(Person.country);
System.out.println(p1.country); // 中国
Person p2 = new Person();
p2.name = "大拿";
System.out.println(p2.country);// 中国
Person p3 = new Person();
p3.name = "小宝";
System.out.println(p3.country);// 中国
}
}
1.2 static修饰方法
静态方法:
可直接通过类名访问
静态方法中不能使用this和super
不能直接访问所属类的实例变量和实例方法
可直接访问类的静态变量和静态方法
实例方法:
通过实例访问
可直接访问所属类的静态变量、静态方法、实例变量和实例方法
静态方法必须被实现,即静态方法必须有方法体。
静态方法:本类中直接访问,其他类通过类名加点访问
关于静态信息和实例信息互相访问规则
同级别互相直接访问
实例访问实例:直接访问
静态访问静态:直接访问
实例访问静态:直接访问
静态访问实例:不能直接访问 必须先new对象(main方法就是最常用的静态方法)
/**
* @author WHD
* @description TODO
* @date 2024/1/9 14:03
* 静态方法:本类中直接访问,其他类通过类名加点访问
*
* 关于静态信息和实例信息互相访问规则
* 同级别互相直接访问
* 实例访问实例:直接访问
* 静态访问静态:直接访问
* 实例访问静态:直接访问
* 静态访问实例:不能直接访问 必须先new对象(main方法就是最常用的静态方法)
*
*/
public class TestStaticMethod {
String name;
static int field;
public static void m1(){
TestStaticMethod tsm = new TestStaticMethod();
System.out.println(field);
System.out.println(tsm.name);
System.out.println("静态方法m1");
tsm.m2();
m4();
}
public static void m4(){
System.out.println("静态方法m4");
}
public void m2(){
m1();
System.out.println(field);
System.out.println(name);
System.out.println(this);
System.out.println(super.toString());
m3();
}
public void m3(){
System.out.println("实例方法m3方法");
}
public static void main(String[] args) {
m1();
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/9 14:04
*/
public class Test {
public static void main(String[] args) {
TestStaticMethod.m1();
}
}
1.3 static修饰代码块
静态代码块:随着JVM加载类而执行,因为类只加载一次,所以静态代码块也只执行一次。
多个静态代码块按照书写顺序执行。
什么时候会使用静态代码块?
初始化数据、做一些前置的操作(连接数据库输入用户名、密码、连接地址、驱动信息等)
什么时候会加载类?
1.创建对象
2.访问类中的静态信息
普通代码块:随着对象的创建而执行
/**
* @author WHD
* @description TODO
* @date 2024/1/9 14:20
* 静态代码块:随着JVM加载类而执行,因为类只加载一次,所以静态代码块也只执行一次。
* 多个静态代码块按照书写顺序执行。
*
* 什么时候会使用静态代码块?
* 初始化数据、做一些前置的操作(连接数据库输入用户名、密码、连接地址、驱动信息等)
*
* 什么时候会加载类?
* 1.创建对象
* 2.访问类中的静态信息
*
* 普通代码块:随着对象的创建而执行
*
*/
public class TestStaticCode {
static int num;
static{
System.out.println("静态代码块2");
}
static{
System.out.println("静态代码块1");
}
{
System.out.println("实例/普通代码块1");
}
{
System.out.println("实例/普通代码块2");
}
public static void main(String[] args) {
System.out.println(num);
TestStaticCode testStaticMethod1 = new TestStaticCode();
TestStaticCode testStaticMethod2 = new TestStaticCode();
}
}
2. static练习题
模拟实现选民投票过程:一群选民进行投票,每个选民只允许投一次票,并且当投票总数达到100时,就停止投票
/**
* @author WHD
* @description TODO
* @date 2024/1/9 14:59
* 选民类
* 模拟实现选民投票过程:一群选民进行投票,每个选民只允许投一次票,并且当投票总数达到100时,就停止投票
*/
public class Voter {
String name; // 选民名称
static int ticketCount = 100; //投票总数100
public boolean voteFor(){
if(ticketCount > 0){
ticketCount--;
System.out.println(name + "投出了一票,还剩余:" + ticketCount + "张票");
return true;
}else{
System.out.println("投票结束");
return false;
}
}
public static void main(String[] args) {
Voter v1 = new Voter();
v1.name = "赵四";
v1.voteFor();
Voter v2 = new Voter();
v2.name = "大拿";
v2.voteFor();
Voter v3 = new Voter();
v3.name = "广坤";
v3.voteFor();
for(int i = 1;i < 200;i++){
Voter v = new Voter();
v.name = i + "号选民";
if(!v.voteFor()){
break;
}
}
}
}
3.方法重写
方法重写 override 方法覆盖 (方法重载 overload)
1.存在于子类中(必须有继承)
2.方法名相同
3.参数列表相同
4.返回值相同 (或者是其子类)
5.访问权限不能严于父类(大于等于父类)
6.父类的实例方法不能重写为静态方法
7.父类的静态方法可以被继承 但是不能被重写
8.不能抛出、声明比父类更多的异常
注解:@Override 用于添加在子类重写父类的方法上方 表示此方法为重写方法
如果没有正确重写 将报错 此注解还可以提高代码的阅读性
/**
* @author WHD
* @description TODO
* @date 2024/1/7 14:13
* 宠物类父类
* 父类中书写共有的属性和方法
*/
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;
}
void print(){
System.out.println("宠物的名字是:" + name);
System.out.println("宠物的健康值是:" + health);
System.out.println("宠物的亲密值是:" + love);
}
public Pet(){
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/7 14:07
* 狗狗类
* 属性:品种
* 行为:打印狗狗信息
*
*/
public class Dog extends Pet {
private String strain;
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public Dog(){
}
/**
* 方法重写 override 方法覆盖 (方法重载 overload)
* 1.存在于子类中(必须有继承)
* 2.方法名相同
* 3.参数列表相同
* 4.返回值相同 (或者是其子类)
* 5.访问权限不能严于父类(大于等于父类)
* 6.父类的实例方法不能重写为静态方法
* 7.父类的静态方法可以被继承 但是不能被重写
* 8.不能抛出、声明比父类更多的异常
*
* 注解:@Override 用于添加在子类重写父类的方法上方 表示此方法为重写方法
* 如果没有正确重写 将报错 此注解还可以提高代码的阅读性
*/
@Override
public void print(){
super.print(); // super.print() print()
System.out.println("狗狗的品种是:" + strain);
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/7 14:10
* 企鹅类
* 属性:性别
* 行为:打印企鹅信息
*
* alt + insert 生成封装的方法
*/
public class Penguin extends Pet {
private String sex;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Penguin(){
}
public void print(){
System.out.println("企鹅的性别是:" + sex);
super.print();
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/7 14:16
*/
public class TestPet {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("大黄");
dog.setHealth(100);
dog.setLove(100);
dog.setStrain("金毛");
dog.print();
}
}
4. Object类
4.1 重写toString方法
直接输出一个对象,将默认调用此对象的toString方法
1.为什么自定义的类,可以直接调用toString方法?
因为所有的类默认继承自Object类 而toString方法是Object类中的方法
2.toString()方法的作用?
返回当前对象相关的字符串信息 : 全限定名 + "@" + hash值
3.为什么我们要重写toString()方法?
我们在自定义的子类中,可以根据需求重写toString方法,实现我们需要的效果
比如:输出一个对象直接打印此对象对应的属性名和属性值 实际开发中都会这么做
4.为什么必须重写toString / 能不能自定义一个方法实现打印属性名+属性值
因为toString方法作为父类中的方法 任何子类都可以继承 以及 重写此方法
使用toString方法 意味着程序结构的统一
/**
* @author WHD
* @description TODO
* @date 2024/1/9 15:47
*
* 直接输出一个对象,将默认调用此对象的toString方法
*
*
* 1.为什么自定义的类,可以直接调用toString方法?
* 因为所有的类默认继承自Object类 而toString方法是Object类中的方法
*
* 2.toString()方法的作用?
* 返回当前对象相关的字符串信息 : 全限定名 + "@" + hash值
*
* 3.为什么我们要重写toString()方法?
* 我们在自定义的子类中,可以根据需求重写toString方法,实现我们需要的效果
* 比如:输出一个对象直接打印此对象对应的属性名和属性值 实际开发中都会这么做
*/
public class Student {
String name;
int age;
char sex;
double height;
public String toString(){
return "Student[name = "+ name +",age = "+ age +",sex = "+ sex +",height = "+ height+"]";
}
public static void main(String[] args) {
Student stu1 = new Student();
stu1.name = "赵四";
stu1.age = 20;
stu1.sex = '男';
stu1.height = 188;
System.out.println(stu1);
System.out.println(stu1.toString());
}
}
4.2 重写equals方法
记住:只要是new出来的对象 地址都不同 即使用==比较都为false
1.为什么我们自定义的类可以直接调用equals()方法?
因为equals方法是Object父类中的方法 所有的类都默认继承自此类 自定义的类也同样
所以可以直接调用继承自父类的方法
2.equals()方法的作用?
默认实现为:比较两个对象的地址值是否相同,我们可以重写自定义比较规则,String类
就是对父类的equals方法进行了重写,将原本的只比较地址,重写为了比较地址并且比较内容
3.==和equals()的区别?
==属于比较运算符;equals()属于Object类中的方法
==比较基本数据类型,比较值;==比较引用数据类型,比较地址
equals()方法只能比较引用数据类型,默认实现也为比较地址
4.为什么我们要重写equals方法呢?
假如有这样的"两个人",名字身份证号都相同,则我们可以认为这"两个人"就是同一个人
我们应该让这样的两个对象 equals比较为true 通过重写equals方法实现
/**
* @author WHD
* @description TODO
* @date 2024/1/10 9:14
* 记住:只要是new出来的对象 地址都不同 即使用==比较都为false
*
* 1.为什么我们自定义的类可以直接调用equals()方法?
* 因为equals方法是Object父类中的方法 所有的类都默认继承自此类 自定义的类也同样
* 所以可以直接调用继承自父类的方法
*
* 2.equals()方法的作用?
* 默认实现为:比较两个对象的地址值是否相同,我们可以重写自定义比较规则,String类
* 就是对父类的equals方法进行了重写,将原本的只比较地址,重写为了比较地址并且比较内容
*
* 3.==和equals()的区别?
* ==属于比较运算符;equals()属于Object类中的方法
* ==比较基本数据类型,比较值;==比较引用数据类型,比较地址
* equals()方法只能比较引用数据类型,默认实现也为比较地址
*
* 4.为什么我们要重写equals方法呢?
* 假如有这样的"两个人",名字身份证号都相同,则我们可以认为这"两个人"就是同一个人
* 我们应该让这样的两个对象 equals比较为true 通过重写equals方法实现
*
*
*/
public class Person {
String name;
String idCard;
public Person(String name, String idCard) {
this.name = name;
this.idCard = idCard;
}
public boolean equals(Object obj){
if(this == obj){
return true;
}
Person p = (Person)obj;
if(this.name.equals(p.name) && this.idCard.equals(p.idCard)){
return true;
}
return false;
}
public static void main(String[] args) {
Person p1 = new Person("赵四", "65243213215652326523325412");
Person p2 = new Person("赵四", "65243213215652326523325412");
System.out.println(p1 == p2); // false
System.out.println(p1.equals(p2)); // false
System.out.println("-------------------------------------------------");
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = str1;
System.out.println(str1 == str2); // false
System.out.println(str1.equals(str2)); // true
// String类对Object类中的equals方法进行了重写 改变了父类中代码的比较规则
}
}
自定义方法模拟实现String类比较内容
/**
* @author WHD
* @description TODO
* @date 2024/1/10 9:41
* 模拟String类编写equals方法
*/
public class TestMyString {
public static void main(String[] args) {
System.out.println(myEquals("abc", "acc"));
}
public static boolean myEquals(String str1,String str2){
// 第一步 先比较地址 因为地址如果是相同的 那么就表示属于同一个对象 字符串内容一定是相同的
if(str1 == str2){
return true;
}
// 获取到str1字符串的长度
int str1Length = str1.length(); // length()方法用于获取字符串的长度
// 获取到str2字符串的长度
int str2Length = str2.length();
// 比较两个字符串的长度 如果长度不同 则直接返回false 即不需要再继续比较了
if(str1Length != str2Length){
return false;
}
// 代码能执行到这里 表示长度是相同的
// 将两个字符串都转换为char数组
char[] v1 = str1.toCharArray(); // toCharArray()方法用于将字符串转换为char数组
char[] v2 = str2.toCharArray();
// 遍历数组 比较相同位置的元素
for(int i = 0;i < v1.length;i++){
// 如果相同为位置的元素 内容不同 则直接中断方法 返回false 即不需要再继续比较了
if(v1[i] != v2[i]){
return false;
}
}
// 代码可以执行到这里 说明 for循环中的return false没有执行
// 即表示整个比较结束 所有的位置内容都相同
return true;
}
}
4.3 重写hashCode方法
1.为什么我们自定义的类可以直接调用hashCode()方法?
因为所有的类默认都继承自Object 而hashCode()方法是从Object类中继承而来的
2.hashCode()方法的作用?
返回当前对象的hash(哈希)值
3.什么是hash值?
hash值是使用杂凑算法 根据当前对象的地址等信息计算出来的一个十进制的int类型的数值
相当于地址的表现形式 不是地址值 Java中的地址值是无法获取到的
4.为什么要重写hashCode()方法?
1: 在默认设定下:两个对象equals比较为true 则hashCode也是相同的 因为hashCode是根据地址计算出来的
equals默认就比较地址 总结:equals比较为true的情况 hashCode一定是相同的
综上所述,所以我们要在重写了equals方法以后 继续重写hashCode方法 以维持以上的默认设定
2: 在后续我们所使用的集合中 默认是以两个对象equals比较为true 并且hashCode相同作为去除重复元素的依据
5.应该如何重写hashCode方法/ 重写hashCode方法依据的条件是什么?
我们要保证在equals方法为true的情况 hashCode相等
目前equals方法重写为比较名字和身份证号
所以重写hashCode方法应该继续使用名字和身份证号计算hash值
这样 才可以保证 :在equals方法为true的情况 hashCode相等
6.String类就是重写了equals方法 并且重写了hashCode
/**
* @author WHD
* @description TODO
* @date 2024/1/10 9:14
* 1.为什么我们自定义的类可以直接调用hashCode()方法?
* 因为所有的类默认都继承自Object 而hashCode()方法是从Object类中继承而来的
* 2.hashCode()方法的作用?
* 返回当前对象的hash(哈希)值
*
* 3.什么是hash值?
* hash值是使用杂凑算法 根据当前对象的地址等信息计算出来的一个十进制的int类型的数值
* 相当于地址的表现形式 不是地址值 Java中的地址值是无法获取到的
*
* 4.为什么要重写hashCode()方法?
* 1: 在默认设定下:两个对象equals比较为true 则hashCode也是相同的 因为hashCode是根据地址计算出来的
* equals默认就比较地址 总结:equals比较为true的情况 hashCode一定是相同的
* 综上所述,所以我们要在重写了equals方法以后 继续重写hashCode方法 以维持以上的默认设定
*
* 2: 在后续我们所使用的集合中 默认是以两个对象equals比较为true 并且hashCode相同作为去除重复元素的依据
*
* 5.应该如何重写hashCode方法/ 重写hashCode方法依据的条件是什么?
* 我们要保证在equals方法为true的情况 hashCode相等
* 目前equals方法重写为比较名字和身份证号
* 所以重写hashCode方法应该继续使用名字和身份证号计算hash值
* 这样 才可以保证 :在equals方法为true的情况 hashCode相等
*
* 6.String类就是重写了equals方法 并且重写了hashCode
*/
public class Person {
String name;
String idCard;
public Person(String name, String idCard) {
this.name = name;
this.idCard = idCard;
}
public boolean equals(Object obj){
if(this == obj){
return true;
}
Person p = (Person)obj;
if(this.name.equals(p.name) && this.idCard.equals(p.idCard)){
return true;
}
return false;
}
public int hashCode(){
int prime = 31; // 权重 重要计算因素
int result = 0; // 最终返回值
result = prime * result + (this.name == null ? 0 : this.name.hashCode());
result = prime * result + (this.idCard == null ? 0 : this.idCard.hashCode());
return result;
}
public static void main(String[] args) {
Person p1 = new Person("赵1四", "6524321321565232652332541");
Person p2 = new Person("赵1四", "6524321321565232652332541");
System.out.println(p1 == p2); // false
System.out.println(p1.equals(p2)); // true
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
System.out.println("-----------------------------------------------");
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1.equals(str2));
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
}
}
为什么计算hashCode使用31?
1.因为JDK就使用31
2.因为31是一个特殊的质数 任何数乘以31 等于这个数 左移5位 减去这个数本身
n * 31 = (n << 5) - n
总结:使用31计算hash值效率更高 但是不是非31不可
/**
* @author WHD
* @description TODO
* @date 2024/1/10 11:02
* 为什么计算hashCode使用31?
* 1.因为JDK就使用31
* 2.因为31是一个特殊的质数 任何数乘以31 等于这个数 左移5位 减去这个数本身
* n * 31 = (n << 5) - n
*/
public class TestPrime {
public static void main(String[] args) {
int a = 11;
System.out.println(a * 31);
System.out.println((a << 5) - a);
}
}
5. 自定义类型的属性
自定义的类,同样可以作为类型使用,属于引用数据类型。
/**
* @author WHD
* @description TODO
* @date 2024/1/10 14:01
* 万物皆对象
*
* 学生类
* 属性 : 名字 年龄 性别 地址
*
* ORM Object Relation Mapping 对象关系映射 未来数据库与Java实体类映射的一个操作
*
* 一对一关系:一个学生对应一个地址
* 一对多关系:一个学生对应多门课程
* 多对一关系:多个爱好属于同一个学生 多个成绩属于同一个学生
* 多对多的关系:多个学生对应多个课程 多个学生对应多个城市
*
*/
public class Student {
private String name;
private int age;
private char sex;
private Address address; // 广东省深圳市 深圳市宝安区 宝安区草围社区 航城街道108号 文化路3005号3楼301
private Hobby[] hobbies; //
public void setHobbies(Hobby [] hobbies){
this.hobbies = hobbies;
}
public Hobby [] getHobbies(){
return hobbies;
}
public void setAddress(Address address){
this.address = address;
}
public Address getAddress(){
return address;
}
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 char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", address=" + address +
'}';
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/10 14:08
* 地址类
* 属性:省份 城市 区域 街道 邮编
*/
public class Address {
private String province;
private String city;
private String area;
private String street;
private String zipCode;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", area='" + area + '\'' +
", street='" + street + '\'' +
", zipCode='" + zipCode + '\'' +
'}';
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/10 14:32
* 爱好
* 属性:爱好类型 爱好名称
*/
public class Hobby {
private String hobbyType; // 爱好类型
private String hobbyName; // 爱好名称
public String getHobbyType() {
return hobbyType;
}
public void setHobbyType(String hobbyType) {
this.hobbyType = hobbyType;
}
public String getHobbyName() {
return hobbyName;
}
public void setHobbyName(String hobbyName) {
this.hobbyName = hobbyName;
}
@Override
public String toString() {
return "Hobby{" +
"hobbyType='" + hobbyType + '\'' +
", hobbyName='" + hobbyName + '\'' +
'}';
}
}
import java.util.Arrays;
/**
* @author WHD
* @description TODO
* @date 2024/1/10 14:21
*/
public class TestStudent {
public static void main(String[] args) {
Student stu1 = new Student();
stu1.setName("赵四");
stu1.setAge(20);
stu1.setSex('男');
Address add1 = new Address();
add1.setProvince("广东省");
add1.setCity("深圳市");
add1.setArea("宝安区");
add1.setStreet("航城街道");
add1.setZipCode("567845");
stu1.setAddress(add1);
System.out.println("---------------------------------------------------------");
System.out.println(stu1);
System.out.println("---------------------------------------------------------");
System.out.println(stu1.getName());
System.out.println(stu1.getAge());
System.out.println(stu1.getSex());
System.out.println(stu1.getAddress());
System.out.println("---------------------------------------------------------");
// 回顾数组的默认值
// 回顾空指针异常的问题
// 使用一个指向为null的引用 继续调用属性 或者 方法 就会出现空指针异常
Hobby [] hobbies = new Hobby[3];
hobbies[0] = new Hobby();
hobbies[0].setHobbyType("电子竞技");
hobbies[0].setHobbyName("LOL");
System.out.println(hobbies[0]);
hobbies[1] = new Hobby();
hobbies[1].setHobbyType("文艺类");
hobbies[1].setHobbyName("唱");
System.out.println(hobbies[1]);
hobbies[2] = new Hobby();
hobbies[2].setHobbyType("文艺类");
hobbies[2].setHobbyName("跳");
System.out.println(hobbies[2]);
stu1.setHobbies(hobbies);
System.out.println(stu1.getHobbies()[0].getHobbyType());
System.out.println(stu1.getHobbies()[0].getHobbyName());
System.out.println("----------------------------");
Hobby[] hobbies1 = stu1.getHobbies();
System.out.println(Arrays.toString(hobbies1));
}
}
6.多态
多态的前提:
1.必须有继承关系
2.必须有方法重写
生活中的多态:同一个事物 因为环境/条件不同 产生不同的结果
程序中的多态:同一个引用类型 指向不同的实例 (父类引用指向子类对象 )
父类引用指向子类对象:
向上转型,此时通过父类引用可以访问子类重写父类,或者继承父类的方法,不能访问子类独有的方法
向上转型三种表现形式
1.父类作为形参 子类作为实参
2.父类作为声明返回值 实际返回值为子类类型
3.父类类型的数组、集合 其元素为子类类型
/**
* @author WHD
* @description TODO
* @date 2024/1/7 14:13
* 宠物类父类
* 父类中书写共有的属性和方法
*/
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;
}
void print(){
System.out.println("宠物的名字是:" + name);
System.out.println("宠物的健康值是:" + health);
System.out.println("宠物的亲密值是:" + love);
}
public void cure(){
System.out.println("宠物看病");
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/7 14:07
* 狗狗类
* 属性:品种
* 行为:打印狗狗信息
*
*/
public class Dog extends Pet {
private String strain;
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public Dog(){
}
@Override
public void print(){
super.print();
System.out.println("狗狗的品种是:" + strain);
}
public void cure(){
System.out.println("狗狗看病,吃药,吃骨头,健康值恢复");
setHealth(100);
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/7 14:10
* 企鹅类
* 属性:性别
* 行为:打印企鹅信息
*/
public class Penguin extends Pet {
private String sex;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Penguin(){
}
public void print(){
System.out.println("企鹅的性别是:" + sex);
super.print();
}
public void cure(){
System.out.println("企鹅看病,打针,吃小鱼,健康值恢复");
this.setHealth(100);
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/10 15:43
* 主人类
* 行为
* 1.带宠物去看病
*/
public class Master {
public void toHospitalWithDog(Dog dog){
dog.cure();
}
public void toHospitalWithPenguin(Penguin penguin){
penguin.cure();
}
// 以上两个方法分别实现了给狗狗和企鹅看病 但是这种方式不符合开闭原则
// 因为后续如果有更多的宠物子类 则必须再添加更多的给宠物看病的方法 太啰嗦了
// 开闭原则:软件设计原则之一 表示程序要对扩展开放 对修改源代码关闭
// 所以,我们应该编写一个方法 用于实现给所有的宠物(现存的 以及 未来新增的)看病
public void toHospitalWithPet(Pet pet){ // Pet pet = dog = new Dog();
pet.cure();
}
}
/**
* @author WHD
* @description TODO
* @date 2024/1/7 14:16
*/
public class TestPet {
public static void main(String[] args) {
Master master = new Master();
Dog dog = new Dog();
dog.setName("大黄");
dog.setLove(100);
dog.setHealth(50);
dog.setStrain("金毛");
master.toHospitalWithDog(dog);
System.out.println(dog.getHealth());
Penguin penguin = new Penguin();
penguin.setName("大白");
penguin.setSex("雌性");
penguin.setLove(100);
penguin.setHealth(20);
master.toHospitalWithPenguin(penguin);
System.out.println(penguin.getHealth());
System.out.println("-------------------------------------------------");
dog.setHealth(10);
penguin.setHealth(10);
master.toHospitalWithPet(dog);
master.toHospitalWithPet(penguin);
System.out.println(dog.getHealth());
System.out.println(penguin.getHealth());
Cat cat = new Cat();
cat.setHealth(20);
master.toHospitalWithPet(cat);
}
}
多态举例:学生携带电脑去学校
/**
* @author WHD
* @description TODO
* @date 2024/1/10 16:08
*/
public class Student {
public void gotoSchool(Computer computer){
computer.coding();
}
public static void main(String[] args) {
Student stu1 = new Student();
NoteBook noteBook = new NoteBook();
IPad iPad = new IPad();
DeskComputer deskComputer = new DeskComputer();
stu1.gotoSchool(noteBook);
stu1.gotoSchool(iPad);
stu1.gotoSchool(deskComputer);
}
}
class Computer{
public void coding(){
System.out.println("电脑敲代码");
}
}
class NoteBook extends Computer{
@Override
public void coding() {
System.out.println("笔记本敲代码,很方便");
}
}
class IPad extends Computer{
@Override
public void coding() {
System.out.println("平板电脑敲代码,不太方便");
}
}
class DeskComputer extends Computer{
@Override
public void coding() {
System.out.println("台式机敲代码,视野很广阔");
}
}