一、多态性
多态在java中的体现是 父类的引用指向子类的对象
格式:
父类类型 变量名 = 子类对象
1、代码案例
vi Person.java
public class Person {public String name;public int age;//新增方法public void eat(){System.out.println("人吃饭");}public void sleep(){System.out.println("人睡觉");}
}
vi Man.java
//Man 类继承Person类
public class Man extends Person {boolean isSmoking;public void eat(){System.out.println("男人吃肉长肌肉");}public void walk(){System.out.println("男人走路");}public void earnMoney(){System.out.println("男人挣钱养家");}}
vi Woman.java
public class Woman extends Person {boolean isBeauty;public void eat(){System.out.println("女人少吃减肥");}public void walk(){System.out.println("女人走路");}public void goShopping(){System.out.println("女人喜欢逛街");}
}
上面我们定义了一个父类,两个子类去继承所有方法和属性,两子类都重写了eat方法,并且拥有自己独立的其他方法,我们先测试一下
vi PersonTest.java
public class PersonTest {public static void main(String[] args) {//未使用多态性Person p1 =new Person();Man m1 = new Man();//使用多态性//在声明子类的时候,使用父类接收Person p2 = new Man();}
}
我们可以将子类赋值给父类的变量,就是多态性,但是要求必须是子类才能我这样做
2、多态性的应用
目前为止我们依旧不知道多态能干啥,只知道用子类能声明给父类,上代码
vi PersonTest.java
public class PersonTest {public static void main(String[] args) {//未使用多态性Person p1 =new Person();Man m1 = new Man();//使用多态性//在声明子类的时候,使用父类接收Person p2 = new Man();//多态性的应用p2.eat();}
}
返回
男人吃肉长肌肉
有了一丝明悟,多个子类存在相同方法时,根据传入的子类自动区分不同子类下的方法
我们将man赋值person中去调用eat,发现他调用的还是man,但是我们在idea上点住ctrl 去点击eat发现跳转到person下面了
3、实现多态的条件
1、继承关系#存在继承关系的类之间才能够使用多态性#多态性通常通过一个父类用变量引用子类对象来实现。2、方法重写#子类必须重写(Override)父类的方法。通过在子类中重新定义和实现父类的方法#可以根据子类的特点行为改变这个方法的行为,如猫和狗吃东西的独特行为。3、父类引用指向子类对象#使用父类的引用变量来引用子类对象。这样可以实现对不同类型的对象的统一操作#而具体调用哪个子类的方法会在运行时多态决定
4、多态案例展示
vi Main.java
class Animal {public void sound() {System.out.println("动物发出声音");}
}class Dog extends Animal {@Overridepublic void sound() {System.out.println("狗发出汪汪声");}
}class Cat extends Animal {@Overridepublic void sound() {System.out.println("猫发出喵喵声");}
}public class Main {public static void main(String[] args) {Animal animal1 = new Dog(); // 父类引用指向子类对象Animal animal2 = new Cat(); // 父类引用指向子类对象animal1.sound(); // 输出:狗发出汪汪声animal2.sound(); // 输出:猫发出喵喵声}
}
如果我们测试某个类型,比如动物类下面的犬类,那么拿到的类属性都是犬科下面的,如果什么都不声明,直接调用类,那么就是访问的类本身下面的,除非有子类以父类的形式赋值,不然都会优先调用默认的父类的方法,所以说想要多态,就得使用继承和重写才能去实现多态
5、多态的弊端
二、object类
在java中,如果没有定义类继承与那个父类,那么他会默认继承object类,java.lang.object可以理解为所有类的一个超类
1、object说明
2、objects的常用方法
clone(): 复制对象equals(Object obj): 用于比较对象是否相等finalize(): 在对象被垃圾回收前调用getClass(): 返回对象的运行时类hashCode(): 返回对象的哈希码值toString(): 返回对象的字符串表示。wait(), notify(), notifyAll(): 用于线程同步。
1、clone 克隆
1、在Person类中实现Cloneable接口。2、重写clone()方法,并在方法中调用super.clone()进行对象的浅拷贝。3、处理CloneNotSupportedException异常
vi Person.java
public class Person implements Cloneable {public String name;public int age;public void eat() {System.out.println("人吃饭");}public void sleep() {System.out.println("人睡觉");}//实现Cloneable接口,并重写了clone()方法以支持对象的克隆@Overridepublic Object clone() {try {return super.clone();} catch (CloneNotSupportedException e) {// 处理异常e.printStackTrace();return null;}}
}
vi PersonTest.java
public class PersonTest {public static void main(String[] args) {// 未使用多态性Person p1 = new Person();p1.name = "Alice";p1.age = 30;// 使用克隆创建新对象Person p2 = (Person) p1.clone();p2.name = "Bob";p2.age = 25;// 输出两个对象的信息System.out.println("Person 1 - Name: " + p1.name + ", Age: " + p1.age);System.out.println("Person 2 - Name: " + p2.name + ", Age: " + p2.age);//输出两个类的地址System.out.println("Person 1 - Name: " + p1.name + ", class: " + p1);System.out.println("Person 2 - Name: " + p2.name + ", class: " + p2);}
}
返回
Person 1 - Name: Alice, Age: 30
Person 2 - Name: Bob, Age: 25
Person 1 - Name: Alice, class: Person@5b480cf9
Person 2 - Name: Bob, class: Person@6f496d9f
2、finalize 垃圾回收
vi Person.java
public class Person implements Cloneable {public String name;public int age;public void eat() {System.out.println("人吃饭");}public void sleep() {System.out.println("人睡觉");}@Overrideprotected void finalize() throws Throwable {try {// 执行清理操作,例如关闭资源等System.out.println("对象被垃圾回收前执行finalize()方法");} finally {super.finalize();}}@Overridepublic Object clone() {try {return super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}
}
vi PersonTest.java
public class PersonTest {public static void main(String[] args) {// 未使用多态性Person p1 = new Person();p1.name = "Alice";p1.age = 30;// 使用克隆创建新对象Person p2 = (Person) p1.clone();p2.name = "Bob";p2.age = 25;// 输出两个对象的信息System.out.println("Person 1 - Name: " + p1.name + ", Age: " + p1.age);System.out.println("Person 2 - Name: " + p2.name + ", Age: " + p2.age);// 手动置空对象引用,触发垃圾回收p1 = null;p2 = null;// 强制调用垃圾回收System.gc();}
}
在这个修改后的代码中,我们在
Person
类中重写了finalize()
方法,在该方法中执行了清理操作。在PersonTest
类的main
方法中,我们创建了两个Person
对象p1
和p2
,然后手动将对象引用置空,以便触发垃圾回收。最后,通过调用System.gc()
强制进行垃圾回收,从而使系统调用对象的finalize()
方法这个方法在1.9之后会提示过时了,但是还能用,过时的原因是内部可能出现循环引用,导致此对象不能回收
3、equals 判断地址值是否相同
vi PersonTest.java
public class PersonTest {public static void main(String[] args) {// 未使用多态性Person p1 = new Person();Person p2 = new Person();//对比p1和p2的地址值,如果不同则返回false//equals 的作用是判断两个变量的地址值是否相同,因为new了两个地址值所以一定是不同的System.out.println(p1.equals(p2));System.out.println(p1);System.out.println(p2);}
}
注意
自定义的类在没有重写Object中的equals方法的情况下,调用的就是object类中声明的equals,比较两个对象的引用地址是否相同,或者说比较两个对象是否指向了堆空间中的同一个对象实体,在实际开发中,针对自定义的类,镜像会去判断两个对象是否equals,而此时主要判断两个对象的属性值是否相等,所以我们需要重写object类的equals方法,关于如何重写,推荐调用idea自动实现
在IntelliJ IDEA中可以使用快捷键快速生成和重写equals()方法。以下是在IDEA中使用快捷键的步骤:1、在Person类中,将光标放在类名或类中的任何位置。2、按下快捷键Alt + Insert(或者在菜单栏中选择Code -> Generate...)。3、在弹出的菜单中选择equals() and hashCode()选项。4、在弹出的对话框中勾选需要比较的属性(如name和age),然后点击OK。5、IDEA会自动生成equals()和hashCode()方法的代码。
4、tostring 对象转换字符串
方法是用于将对象转换为字符串表示形式的方法。当我们需要以字符串形式输出对象的内容时,可以重写
toString()
方法来定义对象的字符串表示方式。
vi PersonTest.java
public class PersonTest {public static void main(String[] args) {// 未使用多态性Person p1 = new Person();System.out.println(p1.toString());}
}
返回
Person@3b07d329
看起来和我们直接System.out.println执行的效果是一样的,其实他下面就是调用的tostring
像string file date 或者包装类等object的子类,他们都重写了object类的tostring() 在调用tosring是返回当前对象的实体内容
重写tostring
5、static 类变量(公共变量)
vi Person.java
public class Person {public String name;public Person(String name){this.name = name;}
}
vi PersonTest.java
public class PersonTest {public static void main(String[] args) {// 未使用多态性Person p1 = new Person("你好1");Person p2 = new Person("你好2");}
}
在上面的代码中变量name是一个实例/成员变量,他属于类的每一个对象,p1中的变更对p2没有影响,如果想要让成员变量被类的所有实例共享,就用static修饰即可,称为类变量
1、static格式
2、静态变量案例
vi Person.java
class Person {//变更为静态变量static String name;}
vi PersonTest.java
public class PersonTest {public static void main(String[] args) {// 未使用多态性Person p1 = new Person();Person p2 = new Person();p1.name = "123";System.out.println(p1.name);System.out.println(p2.name);}
}
我们给公共变量static赋值后,哪怕是不同的对象中也存在
3、静态方法
静态方法随着类的加载而加载,可以通过 “类.静态方法" 直接调用
vi Person.java
class Person {//变更为静态变量static String name;//添加静态方法public static void show(){System.out.println("你好");}}
静态方法不需要声明new,可以直接调用
vi PersonTest.java
public class PersonTest {public static void main(String[] args) {// 静态方法不需要声明new 直接调用Person.show();}
}
静态方法可以直接在其他方法中通过方法名调用
vi Person.java
class Person {//变更为静态变量static String name;//添加静态方法public static void show(){System.out.println("你好1");}public static void get(){//调用静态方法show();System.out.println("你好2");}}
vi PersonTest.java
public class PersonTest {public static void main(String[] args) {// 静态方法不需要声明new 直接调用Person.get();}
}
4、什么时候使用静态