方法重写(overriding)与多态 外加equals

方法重写(覆盖)(overriding)
什么是方法重写?
子类根据需求对从父类继承的方法进行重新编写
重写时,可以用super.方法的方式来保留父类的方法

构造方法不能被重写
静态方法不能被重写

方法重写规则

  1. 在父子类中
  2. 方法名相同
  3. 参数列表相同

以下三点一般都保持与父类一致:

  • 返回值类型相同或者是其子类 (小于等于父类、不能增强能力)
  • 访问权限不能严于父类(不能降低权限)

当方法访问修饰符为private,那就不能用,
因为私有方法不能继承和不能重写。

当父类方法为私有,那子类就见不到父类的私有方法,
如果定义一个和父类中私有方法同名的方法,则不是覆盖,是个新方法。

  • 不能抛出比父类方法更多的异常(不能扩大灾难)

重写举例

public class Pet {
	String name;
	void show() {
		System.out.println("我叫" + name);
	}
}
public class Cat extends Pet{
	int size;
	void show() {
		super.show();
		System.out.println("长度为:" + size);
	}
}

show方法就是被重写的。

重写是为多态做铺垫的。

当没有继承 默认继承Object

注意点

  • 父类的静态方法不能被子类覆盖为非静态方法,
    父类的非静态方法不能被子类覆盖为静态方法
  • 子类可以定义与父类同名的静态方法,以便在子类中隐藏父类的静态方法
    (注:静态方法中无法使用super) 父类的私有方法不能被子类覆盖

举例

public class Fly {
	public static void a() {
		System.out.println("aaa");
	}
}
public class Walk extends Fly{
	public static void a() {
		System.out.println("bbb");
	}
	
	public static void main(String[] args) {
		a();//bbb
		Fly f = new Walk();
		f.a();//aaa
		a();//bbb 
	}
}

如上结果,说明当Fly f = new Walk();时,子类不能重写父类的静态方法,
而是直接调父类的静态方法。

子类实际上只是将父类中的该同名方法进行了隐藏,而非重写。
换句话说,父类和子类中含有的其实是两个没有关系的方法,它们的行为也并不具有多态性。

静态方法可以被重载 不能被重写

方法重写vs方法重载
请添加图片描述
了解Object类

Object类是所有类的父类

public class  Pet  extends Object {
          ……
}

Object类被子类经常重写的方法
请添加图片描述
Object类的toString()方法
在某些情况,如果不写tostring方法,得出的结果会成地址值。
作用:打印实体内容
快捷键:ctrl + shift +s => Generate toString()

@Override
	public String toString() {
		return "Cat [size=" + size + ", name=" + name + "]";
	}

Object类的equals()方法

  • 比较两个对象是否是同一个对象,是则返回true
  • 操作符= =
    简单数据类型,直接比较值。如1== 2
    引用类型,比较两者是否为同一对象
    (1)Object类的equals()方法与==没区别
    (2)当有特殊需求,如认为属性相同即为同一对象时,需要重写equals()
    (3)Java.lang.String重写了equals()方法,把equals()方法的判断变为了判断其值

快捷键:ctrl + shift +s => Generate hashCode() and equals()
举例
假设有个类为Cat,它有两个属性:name 和 age。
自动生成如下:

@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Cat other = (Cat) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

手动生成(方法二)
比如都是Dog类,新建两个dog对象,进行比较
属性有年龄和名字

public boolean equals(Object obj) {
//重写其equals()方法
		if(this == obj) {
	//如果地址相同,则直接相等
			return true;
		}
		if(obj == null) {//判空
			return false;
		}
		if(obj instanceof Dog) {
		//判断类型是否是Dog类
			Dog d = (Dog)obj;
//是Dog类,就可以强转类型转换成Dog类
			return this.age == d.age && 	
 			this.name.equals(d.name);
		}
		return false;
	}

equals的作用(String类的特殊)
用于判断两个变量是否是对同一个对象的引用,
即堆中的内容是否相同,返回值为布尔类型。

面向对象的三特性:封装、继承、多态
面向对象特征:多态
多态
同一个引用类型,使用不同的实例而执行不同操作。

父类类型接收子类对象(子类对象引用父类类型)
父类引用指向子类对象(子类对象赋给父类引用)
前提条件:一般是要调用子类重写的方法时

方法重写是实现多态的基础

为什么使用多态?
频繁修改代码,代码可扩展性、可维护性差
所以需要使用多态优化。
比如:主人给猫看病,但同时又需要给狗看病,这时候就要多态。

生活中的多态
不同类型的打印机打印效果不同

同一种事物,由于条件不同,产生的结果也不同

多态的使用
多态一
举例
Pet是父类,Dog和Cat是子类

Pet d = new Dog();//子类对象引用父类类型Pet c = new Cat();

编译看左边,运行看右边。

多态二
举例

public class Printer {
	void print() {}
}
public class ColorPrinter extends Printer {
	void print() {
		System.out.println("彩色打印机");
	}
}
public class NormalPrinter extends Printer{
	void print() {
		System.out.println("黑白打印机");
	}
}
public class Console {
	void job(Printer p) {//多种形态,多态
		//把父类类型传进来
		p.print();
	}//只管来打印,不管打印什么东西,看需求
	//Printer p = cp; 实际上把地址传进来了
	public static void main(String[] args) {
		Console c =new Console();
		NormalPrinter np = new yNormalPrinter();
		ColorPrinter cp = new ColorPrinter();
		c.job(cp);
	}
}

结果为:彩色打印机

向上转型
向上转型:父类的引用指向子类对象,自动进行类型转换

举例
Pet是父类,dog是子类

 Pet pet = new Dog();
<父类型> <引用变量名> = new <子类型>();

此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法,不是父类的方法
此时通过父类引用变量无法调用子类特有的方法

向下转型
向下转型:将一个指向子类对象的父类引用赋给一个子类的引用,
即:父类类型转换为子类类型。需强制类型转换

<子类型> <引用变量名> = (<子类型> )<父类型的引用变量>;

例如
父类是Pet
子类是Cat 和dog
测试类为:TestPet

public class TestPet {
	Pet d = new Dog();
	Pet c =new Cat();
	Dog d2 = (Dog)d;
//父类去调子类特有的方法,不能直接用,需要强制转换。
	d2.print();
}

在向下转型的过程中,如果没有转换为真实子类类型,会出现类型转换异常

instanceof运算符
instanceof用于判断一个引用类型所引用的对象是否是一个类的实例
(某个对象是否是某个特定类或者是该特定类的子类的一个实例)
如何减少在向下转型的过程中,没有转换为真实子类类型的类型转换异常?
Java中提供了instanceof运算符来进行类型的判断。
使用instanceof时,对象的类型必须和instanceof后面的参数所指定的类在继
承上有上下级关系(并列关系不能转)。

例如
父类是Pet
子类是Cat 和dog(dog里有say())
测试类为:TestPet

Pet d = new Dog();
Pet c = new Cat();
if(c instanceof Dog) {//并列关系  不能转
	Dog d1 = (Dog)c;
	d1.say();
}

实现步骤

1.实现继承
2.父类类型 new 子类对象
3.调用共有的方法
	Pet pet = new Dog();
	if(pet instanceof Dog) {
		pet.xxx()
	}

多态的应用
使用父类作为方法的形参,是Java中实现和使用多态的主要方式
使用父类作为方法的返回值,也是Java中实现和使用多态的主要方式

另一种多态:
设计模式=>代理模式:静态代理

给我传什么,我就给你返回什么对象

举例

public class Father {
	//设计模式=>代理模式:静态代理	
	public Father getInstance(String chose) {
		if(chose.equals("A")) {
			return new A();
		}else if(chose.equals("B")) {
			return new B();
		}else {
			return new Father();
		}
	}
}
class A extends Father{}
class B extends Father{}
import xxx.xxx.xxx.Father;
//father 和 test 不在同一个包
public class Test {
	public static void main(String[] args) {
		Father f =new Father();
		Father ff = f.getInstance("A");
		System.out.println(ff);
	}
	//将A和B隐藏起来,只能通过我这个方式来找到
	//比如你想找我儿子  必须通过我这个父亲
}

结果为:xxx.xxx.xxx.A@15db9742

面试题:==和equals的区别?
== 运算符
1.可以使用在基本数据类型变量(它不是类)和引用数据类型变量中。

2.如果比较的是基本数据类型变量:
比较两个变量保存的数据是否相等。(不一定类型要相同)

PS:基本数据类型运算时不跟布尔类型Boolean结合

如果比较的是引用数据类型变量:
比较两个变量的地址值是否相同,即两个引用是否指向同一个对象实体。

补充:== 符号使用时,必须保证符号左右两边的变量类型一致

equals()方法的使用
1.是一个方法,而非运算符。
2.只能适用于引用数据类型
3.Object类中equals()的定义:

   public boolean equals(Object obj){
        return (this == obj);
   }

说明:Object类中定义的equals()和==的作用是相同的:
比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体。

4.像String、Date、File、包装类等都 重写 Object类中的equals()方法。
重写以后,比较的不是两个引用的地址是否相同,
而是比较两个对象的 “实体内容” 是否相同。
比如String,比的是字符串。

5.通常情况下,所有的类都继承Object,
所以我们自定义的类如果使用equals()的话,结果是false。

但是我们通常是比较两个对象的“实体内容”是否相同。
那么,我们就需要对Object类中的equals()进行重写。
重写的原则:比较两个对象的实体内容是否相同。

重写equals()方法的原则: 对称性、自反性、传递性、一致性

特别地
任何情况下,x.equals(null),永远返回都是“false”
x.equals(不同类型),永远返回都是“false”
null.equals(x);如果null写前面,会出现空指针异常

面试题:比较字符串,为什么用equals?
举例

String s1 = "123";
String s2 = "123";		
System.out.println(s1 == s2);//true
//此时相等,不是内容的相等,而是地址相等。
//在常量区创建
//算是jvm的一个优化,指向同一个地址
//但当S2+="12";时,拼接后,就会指向新的地址
//就是 新建了个地址,然后S2指向了新地址
//原来的也不会丢掉,多次实现,就会造成内存浪费

String str1 = new String("123");
String str2 = new String("123");
System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true
//在堆中创建

equals()方法比较的是两个对象的值 ,
而==比较的两个引用是否指向同一个对象

用=时会在字符常量串常量池中创建,再创建一个string s2=“123”;
会把s1的引用指向已经存在的“123”,而不会再创建一个,引用都是一样。

而用new()时,是从堆内存中开辟一块空间存放,new一次,新建一次
所以 str1 和 str2 是不同的两块空间 所以引用也就不相同的,
所以使用==是错的,那么用equals()方法就是比较内容是否相同。

所以一般比较字符串是用equals,来比较实体内容是否相等。

----2021.08.10

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值