一 多态的概念
多态可以理解为事物存在的多种体现形态。
例如:人有男人和女人两种体现,动物有猫和狗……各种体现。
猫这个对象对应的类型是猫类型,猫 x = new猫();
同时,猫也是动物的一种,也可以把猫成为动物,动物 X = new 猫();
动物是猫和狗具体事物中抽象出来的父类。父类引用指向了子类对象。
多态强调的是在对象上的体现,同一个对象可以是多种类型的实例。
甚至,函数也同样可以具备多态性(重载和覆盖),例如子类和父类的同一个函数就有可能不一样。
二 多态的扩展性
通过多态,可以提高代码的扩展性,留意下面示例代码的function()方法。
1、多态的体现
- 父类的引用,指向了自己的子类对象
- 父类的引用也可以接收自己的子类对象
2、多态的前提
- 必须是类与类之间有关系,要么继承,要么实现
- 通常还需要存在方法的覆盖
3、多态的好处 :多态的出现大大的提高了程序的扩展性
4、多态的弊端 :虽然提高了扩展性,但只能使用父类的引用访问父类中的成员。
package P1;
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("吃鱼");
}
public void CatchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("吃骨头");
}
public void Kanjia() {
System.out.println("看家");
}
}
public class Test {
public static void main(String[] args) {
Animal c = new Cat(); //注意这里,父类引用指向子类对象,这就是多态
c.eat(); //吃鱼
Dog d = new Dog();
d.eat(); //吃骨头
function(c); //吃鱼
}
public static void function(Animal a) { //注意这里
a.eat();
}
}
三 转型
1. 关于转型
向上转型:子类引用的对象转换为父类类型称为向上转型。
向下转型:父类引用的对象转换为子类类型称为向下转型。
2. 具体到代码
以上面“扩展性”的代码进行说明。
(1) Animal c = new Cat(); //类型提升,又称为向上转型。自动完成的。
(2) 如果想要调用猫的特有方法时,可以强制将父类的引用转为子类的类型 → Cat c= (Cat)a;
(3) Animal a = new Animal();
Cat c = (Cat) a;
这是错误的,千万不要出现"将父类对象转为子类类型"这样的操作。
我们能转换的是父类引用指向子类对象时,该引用可以被提升,也可以被强制转换。
规律:多态自始至终都是子类对象在做着变化。
(4) function(Animal a)方法传入狗的实例对象new Dog(),而运行猫的抓老鼠方法,会发生以下的情况:
传参的时候,可以通过编译,但会运行失败,抛ClassCastException类型转换异常。
此时可以用新的关键字,instanceof, if(a instanceof Cat)判断,某个实例是否某种类型。
3. 示例代码
public class DuotaiDemo {
public static void main(String[] args) {
Animal a = new Cat();// 类型提升,又称为向上转型.
a.eat();
// 如果想要调用猫的特有方法时,如何操作?
// 强制将父类的引用转为子类的类型 → 向下转型
// Animal a = new Animal(); 错误
// Cat c = (Cat) a; 不能将父类对象转为子类类型
Cat c = (Cat) a; // 正确做法 : 向下转型
c.CatchMouse();
}
public static void function(Animal a) {
if (a instanceof Dog) {
((Dog) a).Kanjia();
}
a.eat();
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("吃鱼");
}
public void CatchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("吃骨头");
}
public void Kanjia() {
System.out.println("看家");
}
}
4. 关于instanceof关键字
instanceof :用来在运行时指出对象是否是特定类的一个实例。
instanceof可以用的两种一般情况:
1.子类类型有限,例如:人要么是男,要么是女;
2.类型需要其他操作,例如需要判断是否某种类型
注意:判断的时候,不要把父类Animal放最上面,不然一为真就整个判断结束了。
代码如下:
if (a instanceof Animal) {
((Dog) a).Kanjia();
}else if……
四 示例
基础班的学生:学习,睡觉。
高级班的学生:学习,睡觉。
可以将这两类事物进行抽取。
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 DoStudent {
public void doSome(Student stu) {
stu.study();
stu.sleep();
}
}
public class Demo {
public static void main(String[] args) {
Student bs = new BaseStudent();
Student as = new AdvStudent();
doSome(bs);
doSome(as);
doSome(new BaseStudent());
doSome(new AdvStudent());
DoStudent ds = new DoStudent();
ds.doSome(new BaseStudent());
ds.doSome(new AdvStudent());
}
public static void doSome(Student stu) {
stu.study();
stu.sleep();
}
}
注意,DoStudent和它的doSome方法。
可以指挥一批对象执行方法,找到了对象的共同所属类型 Student。
建立父类Student后,在建立相应的工具类DoStudent,在主函数中直接调用工具类即可。
对类型进行抽取导致多态的产生,操作同一个大类型,对大类型中的所有子类型都可以操作。
五 多态中成员的特点
1、在编译时期:参阅引用型变量所属类中是否有调用的方法,如果有,编译通过,如果没有编译失败
2、在运行时期:参阅对象所属的类中是否有调用的方法
3、简单总结就是:成员函数在多态调用时,编译看左边,运行看右边( Fu f = new Zi();编译看Fu,运行看new Zi()所属的类 )
class Fu {
void method1() {
System.out.println("fu method_1");
}
void method2() {
System.out.println("fu method_2");
}
}
class Zi extends Fu {
void method1() {
System.out.println("zi method_1");
}
void method3() {
System.out.println("zi method_3");
}
}
public class Demo {
public static void main(String[] args) {
Fu f = new Zi();
f.method1();// zi-1
f.method2();// fu-2
//f.method3(); // 错误
Zi z = new Zi();
z.method1();// zi-1
z.method2();// fu-2
z.method3();// zi-3
}
}
面试的环节:
在多态中,成员变量的特点,无论编译和运行,都参考左边(引用型变量所属的类)。
在多态中,静态成员函数的特点,无论编译和运行,都参考左边。
class Fu {
int num = 5;
static void method4() {
System.out.println("fu method_4");
}
}
class Zi extends Fu {
int num = 8;
static void method4() {
System.out.println("zi method_4");
}
}
public class Demo {
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.num); // 为 5
Zi z = new Zi();
System.out.println(z.num); // 为 8
f.method4(); // fu
z.method4(); // zi
}
}
输出:
5
8
fu method_4
zi method_4
六 主板示例
需求:电脑运行实例,电脑运行基于主板。
class MainBoard {
public void run() {
System.out.println("MainBoard run");
}
public void useNetCard(NetCard c) {
c.open();
c.close();
}
}
class NetCard {
public void open() {
System.out.println("NetCard open");
}
public void close() {
System.out.println("NetCard close");
}
}
public class Demo {
public static void main(String[] args) {
MainBoard mb = new MainBoard();
mb.run();
mb.useNetCard(new NetCard());
}
}
为了降低各种卡与主板的耦合性,在主板上面预留一些插槽。改进代码如下:
interface Pci {
public void open();
public void close();
}
class MainBoard{
public void run() {
System.out.println("MainBoard run");
}
public void usePci(Pci p) { // Pci p = new NetCard(); 接口型引用指向自己的子类对象
if (p != null) {
p.open();
p.close();
}
}
}
class NetCard implements Pci {
public void open() {
System.out.println("NetCard open");
}
public void close() {
System.out.println("NetCard close");
}
}
class SoundCard implements Pci {
public void open() {
System.out.println("SoundCard open");
}
public void close() {
System.out.println("SoundCard close");
}
}
public class Demo{
public static void main(String[] args) {
MainBoard mb = new MainBoard();
mb.run();
mb.usePci(new NetCard());
mb.usePci(new SoundCard());
}
}
七 扩展示例
需求:数据库的操作,步骤如下:
- 连接数据库 : JDBC Hibernate
- 操作数据库 :c:create r:read u:update d:delete
- 关闭数据库
interface UserInfoDao {
public void add(User user);
public void delete(User user);
}
class UserInfoByHibernate implements UserInfoDao {
public void add(){
1、连接数据库
2、使用sql添加语句添加数据
3、关闭连接
}
public void delete(User user){
1、连接数据库
2、使用sql添加语句删除数据
3、关闭连接
}
}
public class Demo {
public static void main(String[] args) {
// UserInfoByJDBC ui = new UserInfoByJDBC();
UserInfoDao ui = new UserInfoByHibernate();
ui.add(user);
ui.delete(user);
}
}
class UserInfoByJDBC {
public void add(){
1、连接数据库
2、使用sql添加语句添加数据
3、关闭连接
}
public void delete(User user){
1、连接数据库
2、使用sql添加语句删除数据
3、关闭连接
}
}
class UserInfoByHi {
public void add(){
1、连接数据库
2、使用sql添加语句添加数据
3、关闭连接
}
public void delete(User user){
1、连接数据库
2、使用sql添加语句删除数据
3、关闭连接
}
}
八 Object类 - equals
1、类 Object 是类层次结构的根类。每个类都使用 Object 作为超类,所有对象(包括数组)都实现这个类的方法。
2、Object :是所有对象的直接或者间接父类,传说中的上帝
- 该类中定义的是所有对象都具备的功能
- Object没有super
- java认为所有对象都具备比较性
3、equals方法,指示其他某个对象是否与此对象“相等”。实际是比较的对象引用的地址
4、Objedt 类中已经提供了对对象是否相同的比较方法
- 如果自定义类中也有比较相同的功能,没有必要重新定义
- 只要沿袭父类中的功能,建立自己特有比较内容即可,这就是覆盖
- 多态的时候,使用子类特有成员,要注意向下转型
class Demo {
private int num;
Demo(int num) {
this.num = num;
}
public boolean equals(Object obj) {
if (obj instanceof Demo) {
Demo d = (Demo) obj;
return this.num == d.num;
}
return false;
}
}
public class ObjectDemo {
public static void main(String[] args) {
Demo d1 = new Demo(1);
Demo d2 = new Demo(2);
System.out.println(d1.equals(d2));
}
}
九 Object类 - toString
public String toString() :返回该对象的字符串表示。
通常,toString 方法会返回一个“以文本方式表示”此对象的字符串。
结果应是一个简明但易于读懂的信息表达式。建议所有子类都重写此方法。
Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成。
换句话说,该方法返回一个字符串,它的值等于: getClass().getName() +'@'+ Integer.toHexString(hashCode())
返回:该对象的字符串表示形式。
class Demo {
}
public class ObjectDemo {
public static void main(String[] args) {
Demo d1 = new Demo();
System.out.println(d1);//Demo@4f1d0d
System.out.println(d1.getClass().getName()+'@'+Integer.toHexString(d1.hashCode()));//Demo@4f1d0d
}
}