目录
多态
什么是多态?
同类型的对象,执行同一个行为,会表现出不同的行为特征。
多态的常见形式
向上转型:通过子类对象 (小范围) 实例化父类对象 (大范围) , 这种属于自动转换
父类名称 父类对象名称 =new 子类构造器;
Father f =new Son();
向下转型: 通过父类对象 (大范围) 实例化子类对象 (小范围) , 这种属于强制转换
Son s = (Son) f;
多态的形式: 父类类型 对象名称 = new 子类构造器
Animal a = new Dog();
多态中成员访问特点
方法调用:编译看左边,运行看右边。
变量调用:编译看左边,运行也看左边(多态侧重行为多态)
方法调用
Animal
package com.jr.afternoon;
public abstract class Animal {
//不用写吃的方法,因为子类也会重写,所以定义一个抽象的类和方法
public abstract void eat();
// System.out.println("吃得好");
}
Dog(重写父类)
package com.jr.afternoon;
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃的贼快");
}
}
Cat(重写父类)
package com.jr.afternoon;
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃的贼好");
}
}
左边:编译,指的是看Animal里面的方法,Animal里面有"eat()"方法,真正跑的是走真实对象,毕竟Dog里面有方法。
变量调用
Animal
package com.jr.afternoon;
public abstract class Animal {
//不用写吃的方法,因为子类也会重写,所以定义一个抽象的类和方法
public abstract void eat();
// System.out.println("吃得好");
public String name="父类动物";//变量调用
}
Cat
package com.jr.afternoon;
public class Cat extends Animal{
public String name="子类猫";
@Override
public void eat() {
System.out.println("猫吃的贼好");
}
}
Dog
package com.jr.afternoon;
public class Dog extends Animal{
public String name="子类狗";
@Override
public void eat() {
System.out.println("狗吃的贼快");
}
}
Test
走的是父类,印证了 变量调用:编译看左边,运行也看左边
多态的前提
有继承/实现关系;有父类引用指向子类对象;有方法重写
优势
在多态形势下,右边对象可以实现解耦合,便于扩展和维护。
Animal a = new Dog();
Animal a = new Cat();
a.run();//后续业务行为随对象而变,后续代码无需修改
定义方法的时候,使用父类型作为参数,该方法就可以接受父类的一切子类对象,体现出多态的扩展性与便利。
多态下会产生的一个问题
多态下不能使用子类的独有功能。
自动类型转换(从子到父):子类对象赋值给父类类型的变量指向。
强制类型转换(从父到子):
此时必须进行强制类型转换:子类对象变量=(子类)父类类型的变量
作用:可以解决多态下的劣势,可以实现调用子类独有的功能。
Dog独有功能:
package com.jr.afternoon;
public class Dog extends Animal{
public String name="子类狗";
@Override
public void eat() {
System.out.println("狗吃的贼快");
}
public void run(){//独有功能
System.out.println("狗跑的贼溜");
}
}
Test
Animal a=new Dog();
a.eat();
Dog dog= (Dog) a;//从父类类型到子类类型 强转
dog.run();
强制类型转换(从父到子):
必须进行强制类型转换:子类 对象变量=(子类)父类类型的变量
作用:可以解决多态下的劣势,可以实现调用子类独有的功能。
注意:如果转型后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException
Animal a = new Dog();
Cat c = (Cat)c; //出现异常ClassCastException
Java建议强转转换前使用instanceof判断当前对象的真实类型,再进行强制转换。
变量名 instanceof 真实类型
判断关键字左边的变量指向的对象的真实类型,是否是右边的类型或者是其子类类型,是则返回true,反之。
Animal a=new Dog();//Cat Dog tiger
a.eat();
Animal a2=new Cat();
a2.eat();
if (a instanceof Dog){
Dog dog1= (Dog) a;
dog1.run();
}else if (a instanceof Cat){
Cat c= (Cat) a2;
c.climb();
}
instanceof关键字详解
boolean result = obj instanceof Class
obj 是一个对象,Class 表示一个类或接口。obj 是 class 类的实例或者子类实例时,结果 result 返回 true,否则返回 false。
instanceof是 Java 的保留关键字。作用是测试它左边的对象是否是它右边的类的实例,返回boolean的数据类型。
匿名对象
匿名对象就是没有明确的给出名字的对象,是对象的一种简写形式。每次 new 都相当于开辟了一个新的对象,并开辟了一个新的物理内存空间。
类名称 对象名 = new 类名称();
public class Person {
public String name; // 姓名
public int age; // 年龄
// 定义构造方法,为属性初始化
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 获取信息的方法
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
public static void main(String[] args) {
new Person("张三", 30).tell(); // 匿名对象
}
}
案例
输出下图的内容
编写步骤:
1. 定义父类Universe,提供方法doAnything。
2. 定义普通类Star,继承 父类Universe 提供成员发光shine方法
3. 定义普通类Sun,继承Star类,
4. 测试类中,创建Star对象,调用shine方法
5. 测试类中,多态的方式创建Sun对象,调用doAnything方法,向下转型,调用shine方法。
Universe
package com.jr.night;
public class Universe {
public void doAnything(){
System.out.println("我是父类");
}
}
Star
package com.jr.night;
public class Star extends Universe{
@Override
public void doAnything() {//重写父类
System.out.println("星星发光");
}
public void shine(){//特有方法
System.out.println("星星一闪一闪亮晶晶");
}
}
Sun
package com.jr.night;
public class Sun extends Star{
@Override
public void doAnything() {
System.out.println("太阳吸引着9大行星旋转");
}
public void shine(){
System.out.println("光照八分钟,到达地球");
}
}
Test
package com.jr.night;
public class Test {
public static void main(String[] args) {
Universe u=new Star();
Star star=(Star) u;//强制
star.shine();
System.out.println("===================");
//向上
Universe u1=new Sun();
u1.doAnything();
//向下
Sun sun=(Sun) u1;
sun.shine();
}
}
输出
链表---单向链表
单向链表每个节点包含当前节点的数据域和一个指向下一个节点的指针域。
在创建好链表节点之后下来看链表的构造方法和成员变量,成员方法包括一个完整链表的头结点、尾节点以及链表的长度,创建get和set方法、无参构造函数、带参构造函数。
package com.jr.afternoon;
/**
* 单向
*/
public class Node {
private Integer data;
private Node next;
public Integer getData() {
return data;
}
public void setData(Integer data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public Node() {
}
public Node(Integer data, Node next) {
this.data = data;
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
", next=" + next +
'}';
}
}
定义链表的长度、链表的第一个结点,最后一个结点。
package com.jr.afternoon;
public class SuperLinked {
private int size;
private Node first;
private Node last;
}
尾部插入、指定位置
// 无参构造器
public SuperLinked() {
}
// 把数组添加到链表的尾部
public boolean add(Integer data){
// 把传入的数据构建成一个结点
Node node = new Node(data,null);
// 如果现在链表是空的,那我就是第一个结点
if(first == null) {
first = node;
}else {
// 如果链表不是空,那我就是最后一个结点
// 我应该是在原来的last结点后面
// 我是原来last结点的下一个结点
last.setNext(node);
}
last = node;
size++;
return true;
}
public Node getNode(int index){
if(index < 0){
index = 0;
}
if(index >= size - 1){
index = size - 1;
}
// 找到第index个
Node cursor = first;
for (int i = 0; i < index; i++) {
cursor = cursor.getNext();
}
return cursor;
}
运行结果
总结
今日学习了面向对象的第三个特征-----多态、instanceof关键字、匿名对象、单向链表。多态有两种形式:向上转型、向下转型。其中向下转型要进行强制转换,属于父转子,多态的前提是:有继承/实现关系;有父类引用指向子类对象;有方法重写。通过一天学习,我掌握了向上转型和向下转型的使用,并能独立写出一个简单的小案例。开心!开心!开心!但是单向链表没太听明白555555......多看视频回放吧...