今日重点:
目录
一、多态
课上举例子的Animal,Cat,Dog,Person四个类其中隐藏的知识点还没有总结归纳!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
(1)概念
多态是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义,指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。例如买了水果,水果有很多种,苹果、香蕉、梨子......
(2)多态形成条件
1.有继承
2.有重写
3.父类对象指向子类引用
(3)形式(转型)
在JAVA中,继承是一个重要的特征,通过extends关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。那么在这个过程中就存在着多态的应用。存在着两种转型方式,分别是:向上转型和向下转型。
1.向上转型
- 当子类对象赋值给一个父类引用时,即向上转型(多态本身就是向上转型的过程)
- 格式:父类类型 变量名 = new 子类类型();---子转父
- 如:Animal a=new Cat();//Animal 为父类,Cat为子类
- 可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
- 说明:向上转型时,子类对象当成父类对象,只能调用父类的功能,如果子类重写了父类中声明过的方法,方法体执行的就是子类重过后的功能。但是此时对象是把自己看做是父类类型的,所以其他资源使用的还是父类型的。
比如:花木兰替父从军,大家都把花木兰看做她爸,但是实际从军的是花木兰,而且,花木兰只能做她爸能做的事,在军营里是不可以化妆的。
向上转型要点: 自动转型,隐式转换,向上转型就是 父类引用指向子类实例 ,也可以说:子类对象可以赋值给父类 向上转型是安全的 ,因为子类都重写了父类方法, 父类引用指向那个子类,就调用该子类重写方法 向上转型,父类引用只能调用子类重写方法,不能调用子类特有的方法,如果想调用,就必须 向下转型 父类的静态方法是不允许子类重写的
2.向下转型
- 通过强制类型转换格式,将父类引用转为子类格式
- 格式:子类类型 变量名 = (子类类型)父类类型的变量;---父转子
- Parent p = new Child();//向上转型,此时,p是Parent类型
Child c = (Child)p;//此时,把Parent类型的p转成小类型Child- 子类的引用的指向子类对象,过程中必须要采取到强制转型。这个是之前向上造型过的子类对象仍然想执行子类的特有功能,所以需要重新恢复成子类对象。其实,相当于创建了一个子类对象一样,可以用父类的,也可以用自己的。
- 说明:向下转型时,是为了方便使用子类的特殊方法,也就是说当子类方法做了功能拓展,就可以直接使用子类功能。
比如:花木兰打仗结束,就不需要再看做是她爸了,就可以”对镜贴花黄”了
向下转型要点: 强制转型 显式转换 ,向下转型就是子类的引用指父类引用,也可以说,父类强制成子类
就可以调用 子类特有的方法,发生向下转型的前提必须先发生向上转型,才能通过强转再转成子类类型
转型时容易出现异常 ClassCastExcepiton的发生。
防止这种异常的发生,需要用java所提供instanceof (对象比较运算符),给引用变量作类型的校验 :对象名 instanceof 数据类型如果对象属于该类型,就返回true如果对象不属于该类型,就返回false
public class Main {
public static void main(String[] args) {
Person person = new Student();
Student student = new Student(); //向上转型
Student student1 = (Student)person; //向下转型
student.sleep();
student1.sleep();
}
}
class Person{
public void sleep() {
System.out.println("睡觉");
}
}
class Student extends Person{
public void sleep() {
System.out.println("学生睡觉");
}
}
(4)instanceof关键字
instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
import java.util.ArrayList;
import java.util.Vector;
public class Main {
public static void main(String[] args) {
Object testObject = new ArrayList();
displayObjectClass(testObject);
}
public static void displayObjectClass(Object o) {
if (o instanceof Vector)
System.out.println("对象是 java.util.Vector 类的实例");
else if (o instanceof ArrayList)
System.out.println("对象是 java.util.ArrayList 类的实例");
else
System.out.println("对象是 " + o.getClass() + " 类的实例");
}
}
//以上代码运行输出结果为:对象是 java.util.ArrayList 类的实例
(5)多态的使用
编译看左边,运行看右边
有了对象的多态性以后,我们再编译期,只能调用父类声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
前提:多态对象把自己看做是父类类型
1.成员变量: 使用的是父类的
2.成员方法: 由于存在重写现象,所以使用的是子类的
3.静态成员: 随着类的加载而加载,谁调用就返回谁的
使用场景
(6)多态的优缺点
优点:
- 提高了代码的维护性(由继承保证)
- 提高代码复用性(由继承保证)
- 提高了代码的扩展性:新增加的子类不影响已存在类的多态性、继承性以及其他特性
- 安全性:向上转型将子类类型隐藏了起来
缺点:
- 无法使用子类特有的功能(如要使用就要使用向下转型强制类型转换)
参考的博客链接为:
https://blog.csdn.net/llllllkkkkkooooo/article/details/108154477
https://blog.csdn.net/weixin_43884234/article/details/116593803
二、课上案例
(1)object类----无敌方法
这里没有掌握,只是大概了解!!!!!!!!!!!!!!---Ch02
package com.jr.morning;
public class Ch02 {
// 无敌方法
public Object show(Object ... obj){
return true;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Person){
Person person = (Person) obj;
}
return super.equals(obj);
}
public static void main(String[] args) {
Object obj = new Person();
obj = new Ch02();
Ch02 ch02 = new Ch02();
ch02.show("",1,'a',1.5,true,obj);
}
}
(2)匿名对象
这里没有掌握,只是大概了解!!!!!!!!!!!!!!---Ch03
package com.jr.morning;
/**
* 匿名对象
* 语法:
* new 类名();
*
* 功能:和正常的有名字的对象的功能是相同的。
* 依然具备了调用属性,方法的功能。
*
* 使用场景:多数是用在传参,实参,多数情况下配合构造器使用
*
* 好处:节约资源。
*
*/
public class Ch03 {
public void show() {
}
public static void main(String[] args) {
// Ch03 ch03 = new Ch03();
// ch03.show();
// new Ch03().show();
// Dog dog = new Dog();
// dog.setName("旺旺");
// dog.setAge(2);
// System.out.println(dog);
new Dog().setName("黑黑");
new Dog().setAge(1);
System.out.println(new Dog());
}
}
(2)多态练习案例
需求:开学时,来的人: 老师:去办公楼;学生:去寝室楼
放假时,走的人:老师:逛街;学生:坐车回家代码在E:\jiruan\workplace\java\java20220725\Works\src\com\jr\morning\test01
还不能完全掌握理解这个案例!!!!!!!!!!!!!!
(3)在超级数组中运用多态
这里掌握理解一些,但是还差一小部分 !!!!!!!!!!!!!!---Ch04
1.SuperArray类
package com.jsoft.morning.test;
/**
* 超级数组
*/
public class SuperArray {
// 维护一个数组,要考虑的是怎么存
private Object [] array;
// 超级数组的长度
private int size;
// 数组当前的容量
private int capacity;
public SuperArray(){
// array = new Integer[10];
this(10);
// capacity = 10;
}
public SuperArray(int capacity){
array = new Object[capacity];
this.capacity = capacity;
}
// 添加数据,默认添加,在数组的尾部添加
public void add(Object data) {
// 添加时要确保容量足够,如果不够,就需要扩容
ensureCapacity(size + 1);
// 真正的添加数据
array[size++] = data;
}
// 添加数据,传入两个参数
// 在指定位置添加
public void add(int index,Object data){
if(rangeCheck(index)){
ensureCapacity(size + 1);
System.arraycopy(array,index,array,index + 1,size - index);
// 真正的添加数据
array[index] = data;
size++;
}
}
// 删除最后一个数据
public Object remove(){
if(size > 0){
return array[--size];
}
return null;
}
// 删除指定下标位置的元素
public Object remove(int index){
if(rangeCheck(index)){
Object res = array[index];
System.arraycopy(array,index + 1,array,index,(--size - index));
return res;
}
return null;
}
// 修改
public boolean set(int index,Object data) {
if(rangeCheck(index)){
array[index] = data;
return true;
}
return false;
}
// 获取超级数组的长度
public int size(){
return size;
}
// 获取指定下标的元素
public Object get(int index) {
// 判断一下index和合法性
if(rangeCheck(index)){
return array[index];
}
return null;
}
private boolean rangeCheck(int index) {
// index >= 0
// index <= size - 1
return (index >=0 && index <= size - 1);
}
private void ensureCapacity(int needCapacity) {
if(needCapacity > capacity){
capacity = capacity + (capacity >> 1);
Object [] newArray = new Object[capacity];
System.arraycopy(array,0,newArray,0,array.length);
array = newArray;
}
}
}
2.Test类
package com.jsoft.morning.test;
public class Test {
public static void main(String[] args) {
// 创建一个超级数组的对象
SuperArray superArray = new SuperArray(5);
superArray.add(20);
superArray.add(10);
superArray.add(70);
superArray.add(90);
superArray.add("你好");
superArray.add(1,100);
// superArray.remove(5);
superArray.set(3,-1);
for (int i = 0; i < superArray.size(); i++) {
System.out.println(superArray.get(i));
}
}
}
---根据7-23课上超级数组例子基础上修改,链接为:
三、延申----数组算类吗
按照通常的理解来说,Java是一种纯面向对象的语言,所以一切皆对象,对于所有的类型都是对象或者对象的变型。但是数组和其它数据类型却不是一般的对象。
比如一般的对象定义是这样的:
Myclass c1=new Myclass();
一般数组的定义是这样的:
Int[] a =new int[10];
引用类型的数组的定义则为:
Myclass[] c2=new Myclass[5];
从以上我们发现一个问题,数组在声明时是没调用构造函数的(没有小括号!)。java中对象的定义都需要调用构造函数的,而数组却没有,这就表明了数组不是类,或者说不是一个简单的类。分别打印普通类和数组类的实例看一下结果:
package com.jr.morning; public class y { public static void main(String[] args) { y t1 = new y(); y[] arr=new y[9]; Class class1 = y[].class; System.out.println(class1); Class class2 = y.class; System.out.println(class2); System.out.println(class1.getSuperclass()); System.out.println(class2.getSuperclass()); } }
输出结果不相同,可以看出数组类和普通类是不一样的:
但是数组类又有自己的父类,打印数组类的父类。
System.out.println(class1.getSuperclass()); System.out.println(class2.getSuperclass());
结果为:
所以数组是不是类呢?答案是:组类可以算作是一种特殊的类。
1.站在JVM的角度看,是类,在JVM解析数组时,会生成一个数组的类
解析数组
2.站在编译角度,不是类,因为没有类名,没有结构。package com.jsoft.morning; public class Ch04 { public static void main(String[] args) { int [] arr = new int[10];//创建数组 } }
参考博客链接为:https://blog.csdn.net/Crystal7003/article/details/79843067
四、链表
(1)概念
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
1.单链表
数组和单链表之间的区别:
数组:
(1)数组需要维护下标
(2)数组定义时需要指定数组长度
(3)当在数组的某些位置增加和删除元素时,还要编写代码处理元素的移动
(4)时间性能:查找O(1)、插入和删除O(n)
(5)空间性能:需要预分配存储空间,分大了浪费,小了容易溢出单链表:
(1)长度可变,扩展性好
(2)内存利用高(可以不连续)
(3)时间性能:查找O(n)、插入和删除O(1)
(4)空间性能:不需要分配存储空间,只要有就可以分配,元素个数不受限制单链表的逻辑结构如下图所示:
单向链表的缺点分析:
(1)单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。
(2)单向链表不能自我删除,需要靠辅助节点 ,而双向链表,则可以自我删除,所以前面我们单链表删除时节点,总是找到temp,temp是待删除节点的前一个节点。
2.双链表
为了能够实现双向查找的和自我删除的功能,引出了双向链表的概念。双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
双链表的逻辑结构如下图所示:
参考的博客链接为:
https://blog.csdn.net/qq_39240270/article/details/86766945
https://blog.csdn.net/chen_hao_181/article/details/104587170
(2)课上单链表案例
1.Node类
package com.jr.afternoon.test;
/**
* 单向链表
*/
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 +
'}';
}
}
2.SuperLinked类
package com.jr.afternoon.test;
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 boolean add(int index,Integer data) {
Node node = getNode(index);
Node newNode = new Node(data,null);
if(node != null){
// Node next = node.getNext();
// newNode.setNext(next);
newNode.setNext(node.getNext());
node.setNext(newNode);
} else {
// 如果要插入的位置是null,只有一种情况,就是整个链表都是空
first = newNode;
last = newNode;
}
size++;
return true;
}
// 默认删除头部的数据
public boolean removeFirst() {
if(size < 0){
return false;
}
if(first != null){
first = first.getNext();
size--;
}
return true;
}
// 删除尾部的数据
public boolean removeLast(){
if(size <= 0){
return false;
}
if(size == 1){
first = null;
last = null;
size--;
return true;
}
if(last != null){
last = getNode(size - 2);
last.setNext(null);
size --;
}
return true;
}
public boolean remove(int index) {
if(size < 0){
return false;
}
if(size == 1){
first = null;
last = null;
size--;
return true;
}else {
Node node = getNode(index - 1);
node.setNext(node.getNext().getNext());
}
size--;
return true;
}
// 修改指定下标位置的元素
public boolean set(int index,Integer data){
Node node = getNode(index);
node.setData(data);
return true;
}
// 根据下标获取指定的数据
public Integer get(int index) {
return getNode(index).getData();
}
// 获取链表的长度
public int size() {
return size;
}
// 根据下标获取指定的结点
private 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;
}
}
3.测试:Demo类
package com.jr.afternoon.test;
public class Demo {
//测试
public static void main(String[] args) {
SuperLinked superLinked = new SuperLinked();
superLinked.add(1);
superLinked.add(2);
superLinked.add(3);
superLinked.add(100);
superLinked.add(0,-100);
superLinked.removeFirst();
superLinked.removeLast();
superLinked.remove(2);
for (int i = 0; i < superLinked.size(); i++) {
System.out.println(superLinked.get(i));
}
}
}
今日总结:关于多态基本的概念和知识点都能理解掌握,但是对实际的案例的理解还差一些,自己不能独立完整敲出代码,还得继续努力!