一、多态性
多态在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 {
@Override
public void sound() {
System.out.println("狗发出汪汪声");
}
}
class Cat extends Animal {
@Override
public 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()方法以支持对象的克隆
@Override
public 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("人睡觉");
}
@Override
protected void finalize() throws Throwable {
try {
// 执行清理操作,例如关闭资源等
System.out.println("对象被垃圾回收前执行finalize()方法");
} finally {
super.finalize();
}
}
@Override
public 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、什么时候使用静态