类和对象
一、基本特征:
(1)封装:保护内部的操作不被破坏;
(2)继承:在原本的基础之上继续进行扩充;
(3)多态:在一个指定的范围之内进行概念的转换。
面向对象的开发三个过程:OOA(面向对象分析)、OOD(面向对象设计)、OOP(面向对象编程)。
二、类与对象的基本概念
类与对象时整个面向对象中最基础的组成单元。
类:是抽象的概念集合,表示的是一个共性的产物,类之中定义的是属性和行为(方法);
对象:对象是一种个性的表示,表示一个独立的个体,每个对象拥有自己独立的属性,依靠属性来区分不同对象。
类和对象的区别:类是对象的模板,对象是类的实例。类只有通过对象才可以使用,而在开发之中应该先产生类,之后再产生对象。类不能直接使用,对象是可以直接使用的。
三、类与对象的定义和使用
类的成员一般有:初始化块,构造器,属性方法,内部类,枚举类。
-
属性用来说明类具有哪些特征
-
行为用来表示类可以执行哪些动作
要创建对象,必须调用构造器,初始化块可以看做特殊的构造器,无参数传入,创建对象时,会被调用。
构造器
1>创建对象
new 类名();
这里调用的就是构造器!!
2>构造器
构造对象的过程
定义:
修饰符 构造器名字(参数){
//方法体
}
注意:构造器名必须使用类名!!!
没有返回值!!!
每一个类中必须有一个构造方法!!!
3>使用构造器
new 构造器名();
首先,通过类构造一个对象;
然后,执行方法体完成该对象的属性初始化。
4>缺省构造器
如果类中没有定义构造器,系统会在编译时提供一个缺省的无参构造器。
缺省构造器:public 类名(){}
但是,如果你定义了构造器,系统就不会提供缺省构造器了。
构造器:构造方法:所谓的构造方法就是使用关键字new实例化新对象时调用的操作方法。
构造方法重载:
构造方法的调用和对象内存分配几乎是同步完成的。可以利用类中属性进行初始化操作,从而避免setter被重复调用。
成员变量
-
变量
分为两种:成员变量和局部变量。
成员变量:定义在类中,对象的属性。
局部变量:定义在方法体中的变量。
-
成员变量与局部变量的区别
-
声明位置不同:成员变量是定义在类中方法体外,局部变量定义在方法体内。
-
修饰符不同:成员变量前可以使用各种修饰符修饰,局部变量只能使用final。
-
作用范围(作用域)不同:成员变量可以作用所有方法体,局部变量只能作用于当前方法体。
-
缺省值不同:成员变量有缺省值,局部变量没有缺省值。
(3)成员变量缺省值
整数类型,0
浮点类型,0.0
char类型,空白字符
布尔类型,false
引用类型,null
(4)变量重名问题
变量的作用域:一个变量只能在声明它的最小语句组中使用,超出这个语句组就不能使用。
重名规则:
一个类中,所有的成员变量之间,不可以重名。
一个方法中,所有的局部变量之间,不可以重名。
成员变量可以和局部变量重名,方法内直接使用的是局部变量。
案例:
类和对象
一、基本特征: (1)封装:保护内部的操作不被破坏; 面向对象的开发三个过程:OOA(面向对象分析)、OOD(面向对象设计)、OOP(面向对象编程)。 二、类与对象的基本概念 类与对象时整个面向对象中最基础的组成单元。 类:是抽象的概念集合,表示的是一个共性的产物,类之中定义的是属性和行为(方法);
类和对象的区别:类是对象的模板,对象是类的实例。类只有通过对象才可以使用,而在开发之中应该先产生类,之后再产生对象。类不能直接使用,对象是可以直接使用的。
三、类与对象的定义 和使用
类有属性可行为:
定义类使用关键字class完成。 类的成员一般有:初始化块,构造器,属性方法,内部类,枚举类。 要创建对象,必须调用构造器,初始化块可以看做特殊的构造器,无参数传入,创建对象时,会被调用。 构造器 1>创建对象 new 类名(); 这里调用的就是构造器!! 2>构造器 构造对象的过程 定义: 修饰符 构造器名字(参数){ //方法体 } 注意:构造器名必须使用类名!!! 3>使用构造器 new 构造器名(); 首先,通过类构造一个对象; 然后,执行方法体完成该对象的属性初始化。 4>缺省构造器 如果类中没有定义构造器,系统会在编译时提供一个缺省的无参构造器。 缺省构造器:public 类名(){}
但是,如果你定义了构造器,系统就不会提供缺省构造器了。 构造器:构造方法:所谓的构造方法就是使用关键字new实例化新对象时调用的操作方法。 构造方法定义原则:
构造方法重载: 构造方法的调用和对象内存分配几乎是同步完成的。可以利用类中属性进行初始化操作,从而避免setter被重复调用。 成员变量
分为两种:成员变量和局部变量。
成员变量:定义在类中,对象的属性。 局部变量:定义在方法体中的变量。
(3)成员变量缺省值 整数类型,0 浮点类型,0.0 char类型,空白字符 布尔类型,false 引用类型,null
(4)变量重名问题 变量的作用域:一个变量只能在声明它的最小语句组中使用,超出这个语句组就不能使用。
重名规则: 一个类中,所有的成员变量之间,不可以重名。 一个方法中,所有的局部变量之间,不可以重名。 成员变量可以和局部变量重名,方法内直接使用的是局部变量。
参数成员方法-参数
作用:将数据动态传入方法体内使用
定义: 定义在方法名或构造器后的小括号里; 参数的作用域是当前方法体; 参数类型 参数名,..........
和局部变量比较: 参数和局部变量一样,只能作用于当前方法体; 局部变量的数据是固定的,而参数的数据是可以变化的。
赋值: 参数赋值,不是用等号,而是调用方法或构造器时通过小括号传入的。 调用方法或构造器时,如果定义了参数,那么需要传入数据,数据的顺序、类型、数量必须和定义时一致。
名称解释: 形参,定义方法时小括号里面的参数。 实参,调用方法时传入的数据,使用实参给形参赋值。
重名问题:参数可以成员变量重名,不可以和局部变量重名。
在同一类中,出现多个方法的方法名相同但是参数列表必须不同。
参数不同: 数量不同; 数量相同,但是对应顺序的参数类型至少有一个不同。 参数名在调用时,不能作为参数不同的依据。
(3)构造器的重载
在同一个类中允许定义多个构造器,但是参数列表必须不同,这种现象我们叫构造器重载。
主要目的:完成创建对象后的不同初始化需求。
类的定义:首字母大写
类定义完成之后,肯定无法直接使用。如果要使用,必须依靠对象,那么由于类属于引用数据类型,所以对象的产生格式(两种格式)如下: (1)格式一:声明并实例化对象 (2)格式二:先声明对象,然后实例化对象:
对象的产生需要以下三步: 1)、加载类 2)、为对象开辟内存空间 3)、调用合适的构造方法 --->构造方法不止有一个 (3)对象属性的初始化方式:(3种方式) 1)、提供一系列的setter和getter方法 2)、通过构造函数的重载 3)、利用静态代码块和实例代码块(静态代码块和实例代码块,在构造函数之前被调用, 静态代码块只会被初始化一次。
对象的初始化顺序:
静态代码块--》实例代码块--》构造方法
static属性(类属性): 普通属性保存在堆内存中,且每个对象独享属性。 如描述共享属性,只需在属性前加static关键字,此时属性保存在全局数据内存中,不受对象实例化控制,所有对象都可以访问数据区。 在定义类时,以非static属性为主,一般不考虑static属性 static类方法 以static定义的类方法不属于对象,直接通过类名调用。 被static所修饰的代码块加载的时候就会被执行,对于多个被static修饰的代码块,守在前面谁先执行。 如何理解this关键词:this表示对象的引用 使用: this.name 调属性 this.func() 调方法 this(string name,int age) 在一个构造函数中,可以调用另外的构造函数,必须写在第一行 引用数据类型与基本数据类型最大的不同在于:引用数据类型需要内存的分配和使用。所以,关键字new的主要功能就是分配内存空间,也就是说,只要使用引用数据类型,就要使用关键字new来分配内存空间。 当一个实例化对象产生之后,可以按照如下的方式进行类的操作:
从内存的角度分析,两种内存空间的概念: 任何情况下,只要看见关键字new,就表示要分配新的堆内存空间,一旦堆内存空间分配了,里面就会有类中定义的属性,并且属性内容都是其对应数据类型的默认值。 对象实例化对象方式内存表示如下:
程序只声明了Person对象,但并没有实例化Person对象(只有栈内存,并没有对应的堆内存空间),则程序在编译的时候不会出现任何的错误,但是在执行的时候出现了上面的错误信息。这个错误信息表示的是“NullPointerException(空指向异常)”,这种异常只要是应用数据类型都有可能出现。 四、对象引用传递初步分析 引用传递的精髓:同一块堆内存空间,可以同时被多个栈内存所指向,不同的栈可以修改同一块堆内存的内容。 下面通过若干个程序,以及程序的内存分配图,来进行代码的讲解。 对应的内存分配图 垃圾:指的是在程序开发之中没有任何对象所指向的一块堆内存空间,这块空间就成为垃圾,所有的垃圾将等待GC(垃圾收集器)不定期的进行回收与空间的释放。
三大特性之继承
1、概念:继承的目的是实现代码复用,并且派生类可以使用基类的功能,而且派生类可以在不改变原有基类的情况下对这些基类功能进行扩展。
2、实现:继承通过使用extends关键字来实现 class [子类] extends [父类] 3、限制:不允许多重继承,但允许多层继承 一个子类只能继承一个父类,但可通过多层继承,如c继承b的方法,b继承a的方法。 子类对象在实例化之前,一定会首先实例化父亲。 在子类的构造方法中,相当于隐含一个super()语句,在父类提供无构造方法的时候可以不写,但如果父类定义多个构造方法,则必须使用super()语句指明调用的构造方法。 子类会继承父类的所有方法,其中非私有部分属于显示继承,子类可以直接调用;而父类中使用private修饰的私有部分,属于隐式继承,子类无法直接使用,需要通过其他方式调用(如setter和getter)
4、super关键字: 上面说到,如果父类定义多个构造方法,则子类在构造时必须使用super()语句指明要调用的构造方法。并且,super还可以在子类中调用父类的非私有方法和属性。 super和this的区别: 1)super从子类中调用父类的非私有方法和属性,不查找子类而直接调用父类定义。 2)this优先调用本类中的方法和属性,先查找本类,如果本类没有才调用父类。 所以,两者最大的区别在于,super调用的一定是父类的方法,而this却不一定。 5、覆写(override) 所谓覆写,指的就是再子类继承父类时,又定义了与父类相同的方法或者属性,至于为什么要覆写,简单来讲就是继承来的属性无法满足子类的需求,从而重新改造以满足需求。 1)属性的腹泻 子类定义了与父类属性名称完全相同的属性,就是属性的覆写。 这种操作本身没有意义,因为属性一般都使用private封装,子类不知道父类有什么属性,那也就不存在属性覆写的问题。 2)方法的覆写 子类定义了与父类方法名、参数类型及个数完全相同的方法,就是方法的覆写。 需要注意的是,子类覆写的方法不能拥有比父类更严格的访问权限。 访问权限:public》protected》default》private,如果父类的访问权限是public,那么子类只能使用public,如果,父类的访问权限是default,那么子类的权限有public、protected以及default。 重载和重写的区别: 重载:一个类有两个以及两个以上方法名相同的方法,但参数类型,参数个数,参数顺序不同,则称为方法的重载。特别注意返回值类型不同不能重载。 重写:发生在父类和子类之间,子类在继承父类时,又重新定义了与父类方法名相同,参数列表相同,返回值类型相同(协同类型)的方法。需要注意的是新定义的方法访问修饰符的限制不能比父类更加严格。 总之,重载和覆写是Java多态的不同表现。前者是在一个类中多态性的表现,后者在父类和子类两个类之间多态性的表现。 6、final关键字:在Java中final被称为终结器。 1)使用final关键字可以修饰、类方法、属性 修饰类--》成为密封类,不允许继承 修饰变量--》变量变为常量,再声明是必须初始化,不能再次赋值 修饰常量--》常量命名全部使用大写字母,多个单词之间用下划线_隔开 修饰方法--》成为密封方法,不能重写 2)final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会编译报错 3)使用final定义的类不能有子类(String类便具有使用 final定义) 4)final一旦修饰一个类之后,该类的所有方法默认都加上final修饰(不包含成员变量) 三大特性之多态
Java中的多态,核心表现主要为:方法多态和对象多态: 1、方法的多态: 1)方法的重载:调用同一方法名,可以根据参数列表的不同,实现不同的功能。 2)方法的重写:同一父类的方法,不同实例化有不同的表现。 2、对象的多态:(前提是方法覆写) 1)对象的向上转型(自动):父类 父类对象=子类实例 观察向上转型:可以发现,如果子类覆写了父类的方法,那么就会调用父类的方法。核心本质在于使用的是哪个子类(new 在哪里),以及调用的方法是否被子类覆写。 至于向上转型的原因:主要作用是可以实现接收参数的统一 2)对象的向下转型,是强制将对象变为子类对象,我们这样做是为了使用子类的扩充操作。 注意:不是所有的父类对象都可以向下转型,首先一定要发生香山转型,否则转型时会出现ClassCastException,因此向下转型存在安全隐患。 为安全起见,在向下转型之前,需要判断是否发生了向上转型,此时可以使用instanceof关键字判断。 多态总结: 1)对象的多态性的前提是方法重写。 2)通过对象的向上转型实现接收参数的统一。 3)通过向下转型可以实现子类扩充方法的调用(有安全隐患一般不用) 4)两个没有关系的对象不能转型,否则会出现ClassCastException。
|
成员方法-参数
-
参数
作用:将数据动态传入方法体内使用
定义:
定义在方法名或构造器后的小括号里;
参数的作用域是当前方法体;
参数类型 参数名,..........
和局部变量比较:
参数和局部变量一样,只能作用于当前方法体;
局部变量的数据是固定的,而参数的数据是可以变化的。
赋值:
参数赋值,不是用等号,而是调用方法或构造器时通过小括号传入的。
调用方法或构造器时,如果定义了参数,那么需要传入数据,数据的顺序、类型、数量必须和定义时一致。
名称解释:
形参,定义方法时小括号里面的参数。
实参,调用方法时传入的数据,使用实参给形参赋值。
重名问题:参数可以成员变量重名,不可以和局部变量重名。
案例:
package day04;
public class SumDemo {
public void sum(int a,int b) { //int a=10,b=20; System.out.println(a+b); }
public static void main(String[] args) { SumDemo sd=new SumDemo(); sd.sum(1,2); sd.sum(3,4); sd.sum(10,20); } }
|
-
方法的重载
在同一类中,出现多个方法的方法名相同但是参数列表必须不同。
参数不同:
数量不同;
数量相同,但是对应顺序的参数类型至少有一个不同。
参数名在调用时,不能作为参数不同的依据。
package day04;
public class SumDemo {
public void sum() {
System.out.println(10+20); }
public void sum(int a,int b) {
System.out.println(a+b);
}
public void sum(int a,int b,int c) {
System.out.println(a+b+c); }
public void sum(int a,double b) {
System.out.println("我们不一样:"+a+b); }
public void sum(double a,int b) {
System.out.println("我们不一样:"+a+b); }
public void sum(double c,double d) { System.out.println("double 2:"+(c+d)); }
// public void sum(double f,double g) { // System.out.println("double 2:"+(f+g)); // } //
public static void main(String[] args) { SumDemo sd=new SumDemo(); sd.sum(1,2); sd.sum(3,4); sd.sum(10,20);
sd.sum(); sd.sum(1, 2, 3); sd.sum(12.1, 12.2); sd.sum(1,2.0); sd.sum(2.0,1); } }
|
(3)构造器的重载
在同一个类中允许定义多个构造器,但是参数必须不同,这种现象我们叫构造器重载。
主要目的:完成创建对象后的不同初始化需求。
//构造器:构造方法 public Circle() { //构造对象之后,用于初始化这个对象的属性操作 //r=4; }
public Circle(double rr) { //构造对象之后,用于初始化这个对象的属性操作 r=rr; }
public Circle(double rr,double tt) { //构造对象之后,用于初始化这个对象的属性操作 r=rr; t=tt; } |
Circle c1=new Circle(); System.out.println(c1.r); System.out.println(c1.t); System.out.println(c1.PI);
Circle c2=new Circle(3); System.out.println(c2.r); System.out.println(c2.t); System.out.println(c2.PI);
Circle c3=new Circle(3,4); System.out.println(c3.r); System.out.println(c3.t); System.out.println(c3.PI); |
类的定义:首字母大写
class 类名称 {
属性 (变量) ;
行为 (方法) ;
}
类定义完成之后,肯定无法直接使用。如果要使用,必须依靠对象,那么由于类属于引用数据类型,所以对象的产生格式(两种格式)如下:
(1)格式一:声明并实例化对象
类名称 对象名称 = new 类名称 () ;
(2)格式二:先声明对象,然后实例化对象:
-
类名称 对象名称 = null ;
-
对象名称 = new 类名称 () ;
对象的产生需要以下三步:
1)、加载类
2)、为对象开辟内存空间
3)、调用合适的构造方法 --->构造方法不止有一个
(3)对象属性的初始化方式:(3种方式)
1)、提供一系列的setter和getter方法
2)、通过构造函数的重载
3)、利用静态代码块和实例代码块(静态代码块和实例代码块,在构造函数之前被调用, 静态代码块只会被初始化一次。)
对象的初始化顺序:
静态代码块--》实例代码块--》构造方法
static属性(类属性):
普通属性保存在堆内存中,且每个对象独享属性。
如描述共享属性,只需在属性前加static关键字,此时属性保存在全局数据内存中,不受对象实例化控制,所有对象都可以访问数据区。
在定义类时,以非static属性为主,一般不考虑static属性
static类方法
以static定义的类方法不属于对象,直接通过类名调用。
被static所修饰的代码块加载的时候就会被执行,对于多个被static修饰的代码块,守在前面谁先执行。
如何理解this关键词:this表示对象的引用
使用: this.name 调属性
this.func() 调方法
this(string name,int age) 在一个构造函数中,可以调用另外的构造函数,必须写在第一行
引用数据类型与基本数据类型最大的不同在于:引用数据类型需要内存的分配和使用。所以,关键字new的主要功能就是分配内存空间,也就是说,只要使用引用数据类型,就要使用关键字new来分配内存空间。
当一个实例化对象产生之后,可以按照如下的方式进行类的操作:
对象.属性:表示调用类之中的属性;
对象.方法():表示调用类之中的方法。
从内存的角度分析,两种内存空间的概念:
(1)堆内存:保存对象的属性内容。堆内存需要用new关键字来分配空间;
(2)栈内存:保存的是堆内存的地址
任何情况下,只要看见关键字new,就表示要分配新的堆内存空间,一旦堆内存空间分配了,里面就会有类中定义的属性,并且属性内容都是其对应数据类型的默认值。
对象实例化对象方式内存表示如下:
-
Exception in thread "main" java.lang.NullPointerException
-
at com.wz.classandobj.TestDemo.main(TestDemo.java:15)
程序只声明了Person对象,但并没有实例化Person对象(只有栈内存,并没有对应的堆内存空间),则程序在编译的时候不会出现任何的错误,但是在执行的时候出现了上面的错误信息。这个错误信息表示的是“NullPointerException(空指向异常)”,这种异常只要是应用数据类型都有可能出现。
四、对象引用传递初步分析
引用传递的精髓:同一块堆内存空间,可以同时被多个栈内存所指向,不同的栈可以修改同一块堆内存的内容。
下面通过若干个程序,以及程序的内存分配图,来进行代码的讲解。
对应的内存分配图
垃圾:指的是在程序开发之中没有任何对象所指向的一块堆内存空间,这块空间就成为垃圾,所有的垃圾将等待GC(垃圾收集器)不定期的进行回收与空间的释放。
三大特性之继承
1、概念:继承的目的是实现代码复用,并且派生类可以使用基类的功能,而且派生类可以在不改变原有基类的情况下对这些基类功能进行扩展。
2、实现:继承通过使用extends关键字来实现
class [子类] extends [父类]
3、限制:不允许多重继承,但允许多层继承
一个子类只能继承一个父类,但可通过多层继承,如c继承b的方法,b继承a的方法。
子类对象在实例化之前,一定会首先实例化父亲。
在子类的构造方法中,相当于隐含一个super()语句,在父类提供无构造方法的时候可以不写,但如果父类定义多个构造方法,则必须使用super()语句指明调用的构造方法。
子类会继承父类的所有方法,其中非私有部分属于显示继承,子类可以直接调用;而父类中使用private修饰的私有部分,属于隐式继承,子类无法直接使用,需要通过其他方式调用(如setter和getter)
4、super关键字:
上面说到,如果父类定义多个构造方法,则子类在构造时必须使用super()语句指明要调用的构造方法。并且,super还可以在子类中调用父类的非私有方法和属性。
super和this的区别:
1)super从子类中调用父类的非私有方法和属性,不查找子类而直接调用父类定义。
2)this优先调用本类中的方法和属性,先查找本类,如果本类没有才调用父类。
所以,两者最大的区别在于,super调用的一定是父类的方法,而this却不一定。
5、覆写(override)
所谓覆写,指的就是再子类继承父类时,又定义了与父类相同的方法或者属性,至于为什么要覆写,简单来讲就是继承来的属性无法满足子类的需求,从而重新改造以满足需求。
1)属性的腹泻
子类定义了与父类属性名称完全相同的属性,就是属性的覆写。
这种操作本身没有意义,因为属性一般都使用private封装,子类不知道父类有什么属性,那也就不存在属性覆写的问题。
2)方法的覆写
子类定义了与父类方法名、参数类型及个数完全相同的方法,就是方法的覆写。
需要注意的是,子类覆写的方法不能拥有比父类更严格的访问权限。
访问权限:public》protected》default》private,如果父类的访问权限是public,那么子类只能使用public,如果,父类的访问权限是default,那么子类的权限有public、protected以及default。
重载和重写的区别:
重载:一个类有两个以及两个以上方法名相同的方法,但参数类型,参数个数,参数顺序不同,则称为方法的重载。特别注意返回值类型不同不能重载。
重写:发生在父类和子类之间,子类在继承父类时,又重新定义了与父类方法名相同,参数列表相同,返回值类型相同(协同类型)的方法。需要注意的是新定义的方法访问修饰符的限制不能比父类更加严格。
总之,重载和覆写是Java多态的不同表现。前者是在一个类中多态性的表现,后者在父类和子类两个类之间多态性的表现。
6、final关键字:在Java中final被称为终结器。
1)使用final关键字可以修饰、类方法、属性
修饰类--》成为密封类,不允许继承
修饰变量--》变量变为常量,再声明是必须初始化,不能再次赋值
修饰常量--》常量命名全部使用大写字母,多个单词之间用下划线_隔开
修饰方法--》成为密封方法,不能重写
2)final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会编译报错
3)使用final定义的类不能有子类(String类便具有使用
final定义)
4)final一旦修饰一个类之后,该类的所有方法默认都加上final修饰(不包含成员变量)
三大特性之多态
Java中的多态,核心表现主要为:方法多态和对象多态:
1、方法的多态:
1)方法的重载:调用同一方法名,可以根据参数列表的不同,实现不同的功能。
2)方法的重写:同一父类的方法,不同实例化有不同的表现。
2、对象的多态:(前提是方法覆写)
1)对象的向上转型(自动):父类 父类对象=子类实例
观察向上转型:可以发现,如果子类覆写了父类的方法,那么就会调用父类的方法。核心本质在于使用的是哪个子类(new 在哪里),以及调用的方法是否被子类覆写。
至于向上转型的原因:主要作用是可以实现接收参数的统一
2)对象的向下转型,是强制将对象变为子类对象,我们这样做是为了使用子类的扩充操作。
class Person{
public void function(){
System.out.println("父类方法");
}
}
class Student extends Person{
public void function(){
System.out.println("子类方法");
}
public void print(){
System.out.println("子类独有的打印方法");
}
}
public class Demo{
public static void main(String[] args) {
Person person=new Student();
person.function();
//此时只能使用父类定义好的方法,不能使用Student类中的printf方法
Student student = (Student)person; //强制向下转型
student.print(); //现在可以访问子类独有的打印方法
}
}
注意:不是所有的父类对象都可以向下转型,首先一定要发生香山转型,否则转型时会出现ClassCastException,因此向下转型存在安全隐患。
为安全起见,在向下转型之前,需要判断是否发生了向上转型,此时可以使用instanceof关键字判断。
Person person = new Student();
if (person instanceof Student) { //避免ClassCastException
Student student = (Student) person ;
student.print();
}
多态总结:
1)对象的多态性的前提是方法重写。
2)通过对象的向上转型实现接收参数的统一。
3)通过向下转型可以实现子类扩充方法的调用(有安全隐患一般不用)
4)两个没有关系的对象不能转型,否则会出现ClassCastException。