一、多态(就是指父类引用指向子类对象)
定义:可以理解为事物存在的多种体现形态。
动物:猫,狗。。。
多态指的是同一个方法调用,由于对象可能不同可能会有不同的行为。
多态的要点:
1. 多态是方法的多态,不是属性的多态。
2. 多态存在要有三个必要条件:继承,方法的重写,父类引用指向子类对象
3. 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
猫 x = new 猫();
动物 下= new 猫();
-
多态的体现
父类的引用指向了自己的子类对象。(创建对象,赋值)
父类的引用也可以接受自己的子类对象。(参数传递,父类类型) -
多态的前提
必须是类与类之间有关系。要么继承,要么实现。
通常还有一个前提:对象只能调用父类中的方法(在子类中被重写),存在覆盖(子类重写父类方法) -
多态的好处
多态的出现大大提高了程序的扩展性。 -
多态的弊端:
虽然提高了扩展性,但是只能使用父类的引用访问父类中的成员。 -
多态的应用
-
多态的出现代码中的特点(多态使用的注意事项)
public abstract class Animal {
public abstract void eat();
}
class Cat extends Animal
{
public void eat()
{
System.out.println("吃鱼");
}
public void catchMouse()
{
System.out.println("抓老鼠");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("啃骨头");
}
public void kanJia()
{
System.out.println("看家");
}
}
class Pig extends Animal
{
public void eat()
{
System.out.println("饲料");
}
public void gongDi()
{
System.out.println("拱地");
}
}
常规写法:
class AnimalDemo
{
public static void main(String[] args) {
Cat c = new Cat();
c.eat();
Dog d = new Dog();
d.eat();
}
public static void function(Cat c) //只接受Cat引用,不接受Animal c = new Cat()中的c引用
{
c.eat();
}
public static void function(Dog d)
{
d.eat();
}
public static void function(Pig p)
{
p.eat();
}
}
直接调用代码
function(new Cat());
function(new Dog());
提高代码的复用性,多态技术
class AnimalDemo
{
public static void main(String[] args) {
Animal a = new Cat();
//Animal a = new Animal();// 错误,抽象类不能实例化
}
public static void function(Animal a) //相当于Animal a = new Cat();
{
a.eat();
}
}
多态中的类型转换
class AnimalDemo
{
public static void main(String[] args) {
Animal a = new Cat();
*/
Animal a = new Cat(); //类型提升:向上转型,转成父类型
a.eat();
//如果想要调用猫中的特有方法时,如何操作?
//强制将父类的引用转成子类类型,向下转型
Cat c = (Cat)a;
c.catchMouse();
//不能出现下列强制类型转换
/* 不可以将父类对象转成子类类型
我们能转换的时父类引用指向自己子类对象时,该引用可以被提升,也可以被强制转换。
多态至始至终都是子类对象在做着变换的
Animal a = new Animal(); //再说了,抽象类不能实例化。
Cat c = (Cat)a;
*/
//稍后介绍反射可以解决不能调用子类方法。
function(c); //
function(new Dog());
function(new Pig());
}
public static void function(Animal a) //相当于Animal a = new Cat();
{
a.eat();
}
}
总结转型
Fu f = new Zi(); //类型提升,向上转型
在内存中创建的是子类对象,但却表现为父类引用,
只能调用父类方法,但实际上是调用的子类重写的方法。
遗憾的是:不能调用子类中特有的方法。
若想调用子类特有的方法:
Zi z = (Zi)f; //强制类型转化,把他变成内存中实际的对象。
调用子类特有的方法:
class AnimalDemo
{
public static void main(String[] args) {
Animal a = new Cat();
//Animal a = new Animal();// 错误,抽象类不能实例化
}
public static void function(Animal a) //相当于Animal a = new Cat();
{
a.eat();
if(a instanceof Cat) //instanceof判断类型,作用:使用特定类型中的特有方法
{
Cat c = (Cat)a;
a.catchMouse();
}
else if(a instanceof Dog)
{
Dog d = (Dog)a;
d.kanJia();
}
else
{
Pig p = (Pig)a;
p.gongDi();
}
}
代码复用方法:
public abstract class Student {
public abstract void study();
public void sleep()
{
System.out.println("躺着睡"); //正常情况下这么做,不正常的学元可以复写
}
}
class BaseStudent extends Student
{
public void study()
{
System.out.println("base study");
}
public void sleep()
{
System.out.println("坐着睡");
}
}
class AdvStudent extends Student
{
public void study()
{
System.out.println("adv study");
}
}
class DuoTaiDemo
{
public static void main(String[] args) {
BaseStudent bs = new BaseStudent();
bs.study();
bs.sleep();
AdvStudent as = new AdvStudent();
as.study();
as.sleep();
}
}
复用代码创建类:
class DoStudent
{
public void doSome(Student stu)
{
stu.study(); //该函数只能调用基类Student中的方法,子类中特有方法不能调用
stu.sleep(); //执行的也是子类中重写的方法
}
}
class DuoTaiDemo
{
public static void main(String[] args) {
DoStudent ds = new DoStudent();
ds.doSome(new BaseStudent);
ds.doSome(new AdvStudent);
}
}
多态中代码的特点:
静态属于类,非静态属于对象(实例化)
1. 非静态成员函数的特点
在编译时期:参阅引用型变量所属的类中是否有调用的方法,如果有,编译通过,如果没有编译失败(简单的说就是只能调用基类方法)
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结:非静态在多态中的调用时,编译看左边,运行看右边。‘
2. 成员变量的特点:
无论编译和运行,都参考左边(引用变量所属的类)。
3. 在多态中,静态成员函数的特点:
无论编译和运行,都参考左边
接口与多态联合使用,可以降低代码的耦合性(减少依赖)
示例1:
需求:电脑运行实例,电脑运行基于主板。
基本想法如下:
class MainBoard {
public void run()
{
System.out.println("mainboard run");
}
public void useNetCard(NetCard nc)
{
nc.open();
nc.close();
}
public void useNetCard(SoundCard sc)
{
sc.open();
sc.close();
}
}
class NetCard
{
public void close() {
System.out.println("netcard open");
}
public void open() {
System.out.println("netcard close");
}
}
class SoundCard
{
public void close() {
System.out.println("SoundCard open");
}
public void open() {
System.out.println("SoundCard close");
}
}
class DuoTaiDemo5{
public static void main(String[] args) {
MainBoard mb = new MainBoard();
mb.run();
mb.useNetCard(new NetCard());
mb.useNetCard(new SoundCard());
}
}
使用多态和接口提高扩展型,降低了耦合度,提高了代码的复用性
interface PCI
{
public abstract void open();
public abstract void close();
}
class MainBoard {
public void run()
{
System.out.println("mainboard run");
}
public void usesPCI(PCI p) // PCI p = new NerCard() 多态:接口型引用指向自己的子类对象
{
if(p!=null)
{
p.open();
p.close();
}
}
}
class NetCard implements PCI
{
@Override
public void close() {
System.out.println("netcard open");
}
@Override
public void open() {
System.out.println("netcard close");
}
}
class SoundCard implements PCI
{
@Override
public void close() {
System.out.println("SoundCard open");
}
@Override
public void open() {
System.out.println("SoundCard close");
}
}
class DuoTaiDemo5{
public static void main(String[] args) {
MainBoard mb = new MainBoard();
mb.run();
mb.usesPCI(null);
mb.usesPCI(new NetCard());
mb.usesPCI(new SoundCard());
}
}
示例2:
需求:数据库的操作,数据是用户信息。
- 连接数据库。 JDBC,Hibernate
- 操作数据库:增删改查(create,read,update,delete)
- 关闭数据库连接
interface UserInfoDao
{
public abstract void add(User user);
public abstract void delete(User user);
}
class UserInfoByJDBC implements UserInfoDao
{
@Override
public void add(User user)
{
// 1. JDBC连接数据库
// 2. 使用sql添加语句添加数据
// 3. 关闭数据库
}
@Override
public void delete(User user)
{
// 1. JDBC连接数据库
// 2. 使用sql添加语句删除数据
// 3. 关闭数据库
}
}
class UserInfoByHibernate implements UserInfoDao
{
@Override
public void add(User user)
{
// 1. JDBC连接数据库
// 2. 使用sql添加语句添加数据
// 3. 关闭数据库
}
@Override
public void delete(User user)
{
// 1. JDBC连接数据库
// 2. 使用sql添加语句删除数据
// 3. 关闭数据库
}
}
class User
{
}
public class DBOperate {
public static void main(String[] args) {
UserInfoDao ui = new UserInfoByJDBC();
//UserInfoDao ui = new UserInfoByHibernate();
User user = new User();
ui.add(user);
ui.delete(user);
}
}
二、Object类
Object:是所有对象的直接或者间接父类,传说中的上帝。
该类中定义的肯定时所有对象都具备的功能。
“==”:代表比较双方是否相同。如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。
- equals方法:对象内容相等
public class ObjectDemo {
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = new Demo();
Demo d3 = d1;
Person p = new Person();
System.out.println(d1.equals(d2)); //其实比较的是内存中的地址
System.out.println(d1.equals(d3));
System.out.println(d1.equals(p));
}
}
Object类中已经提供了对对象是否相同的比较方法。
如果自定义类中也有比较相同的功能,已经没有必要重新定义。
只要沿袭父类中的功能,建立自己特有比较内容即可。这就是覆盖。
下面给出编写一个完美的equals方法的建议:
1. 显式参数命名为otherObbject,稍后需要将它转化成另一个叫做other的变量。
2. 检测this与otherObbject是否引用同一个对象:
if(this==otherObject) return true;
因为计算这个等式要比一个一个地比较类中的域所付出的代价小得多。
3. 检测otherObject是否为null,如果为null,返回fal。这个检测很有必要。
if(otherObject==null) return false;
4. 比较this与otherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用getClass检测:
if(getClass()!=otherObject.getClass()) return false;
如果所有子类都拥有统一的语义,就使用instanceof检测:
if(!otherObject instanceof ClassName) return false;
5. 将otherObject转化为相应的类类型变量:
ClassNmae other = (ClassNmae)otherObject
6. 现在开始对所有需要比较的域惊醒比较了。使用==比较基本类型域,就使用equals比较对象域。如果所有域都匹配,就返回true,否则就返回false.
return field1 == other.field1
&& Objects.equals(field2,other.field2)
&& .......
如果在子类中重新定义equals,就要在其中包含调用super.equals(other).
提示:对于数组类型的域,可以使用静态的Arrays.equals方法检测相应的数组元素是否相等。
@Override
public boolean equals(Object otherObject) {
if(this == otherObject)
return true;
if(otherObject == null)
return false;
if(getClass() != otherObject.getClass())
return false;
Employee other = (Employee)otherObject;
return Objects.equals(name,other.name)
&& salary == other.salary
&& Objects.equals(hireDay,other.hireDay);
}
String中的equals方法的重写
public boolean equals(Object anObject) {
if (this == anObject) { //是否是同一对象
return true;
}
if (anObject instanceof String) { //是否是同一类型
String aString = (String)anObject; //向下转型
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
- hashCode方法:
散列码(hash code)是由对象导出的一个证书值,散列码是没有规律的。不同对象的hashCode()基本上不会相同。
由于hashCode方法定义在Object类中,因此每个对象有一个默认的散列码,其值为对象的存储地址。
Java7中有一个改进,首先最好使用null安全的方法Objects.hashCode。如果其参数为null,这个方法会返回0,否则返回对参数调用的hashCode()的结果。
public int hashCode()
{
return 7*Objects.hashCode(name)
+ 11*new Double(salary).hashCode()
+ 13 * Objects.hashCode(hireDay);
}
还有更好的方法,需要对组合多个散列值时,可以调用Objects.hash并提供多个参数。
public int hashCode()
{
return Objects.hash(name,salary,hireDay);
}
Equals与hashCode的定义必须一直:如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。例如:如果定义的Employee。equals比较雇员的ID,那么hashCode方法就需要散列ID,而不是雇员的姓名和存储地址。
Employee
package Ch5;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Objects;
public class Employee {
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year,month -1, day);
hireDay = calendar.getTime();
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Date getHireDay() {
return hireDay;
}
public void setHireDay(Date hireDay) {
this.hireDay = hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
@Override
public int hashCode() {
return Objects.hash(name, salary, hireDay);
}
@Override
public boolean equals(Object otherObject) {
if(this == otherObject)
return true;
if(otherObject == null)
return false;
if(getClass() != otherObject.getClass())
return false;
Employee other = (Employee)otherObject;
return Objects.equals(name,other.name)
&& salary == other.salary
&& Objects.equals(hireDay,other.hireDay);
}
@Override
public String toString() {
return getClass().getName() + "[name = " + name +", salary = " + salary + ", hireDay = "
+ hireDay + "]";
}
}
Manage.java
package Ch5;
public class Manager extends Employee {
private double bonus;
public Manager(String n, double s, int year,int month, int day)
{
super(n,s,year,month,day);
bonus = 0;
}
@Override
public double getSalary() {
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double b)
{
bonus = b;
}
@Override
public int hashCode() {
return super.hashCode() + 17 * Double.valueOf(bonus).hashCode();
}
@Override
public boolean equals(Object otherObject) {
return super.equals(otherObject);
}
@Override
public String toString() {
return super.toString() + "[bonus = " + bonus + "]";
}
}
Test.java
package Ch5;
public class EqualsTest {
public static void main(String[] args) {
Employee a1 = new Employee("Alice Adams", 75000,1987,12,15);
Employee a2 = a1;
Employee a3 = new Employee("Alice Adams", 75000,1987,12,15);
Employee b = new Employee("Bob Brands", 50000, 1989,10,1);
System.out.println("a1 == a2 : " + (a1 == a2) );
System.out.println("a1 == a3 : " + (a1 == a3));
System.out.println("a1.equals(a2) : " + (a1.equals(a2)));
System.out.println("a1.equals(a3) : " + (a1.equals(a3)));
System.out.println("a1.equals(Bob) : " + a1.equals(b));
Manager carl = new Manager("carl Cracker", 80000,1987,12, 15);
Manager boss = new Manager("carl Cracker", 80000,1987,12, 15);
boss.setBonus(5000);
System.out.println("boss.toString():" + boss);
System.out.println("carl.equals(boss): " + carl.equals(boss));
System.out.println("a1.hashCode(): " + a1.hashCode()); //equals相同则hashCode必相等
System.out.println("a3.hashCode(): " + a3.hashCode());
System.out.println("bob.hashCode(): " + b.hashCode());
System.out.println("carl.hashCode(): " + carl.hashCode());
System.out.println("boss.hashCode(): " + boss.hashCode());
}
}
- toString()方法