Java:进一步理解多态性
每博一文案
有人说我心里有事,但我谁也不想说,沉默不是没有情绪,而是我明白了,说了又没有意义,
比起诉说的委屈和不甘,沉默或许更好。当我们经历越多真实与虚假,看清了世界的真相后,
不仅没有那么多的酸性。反而会变得越来越沉默,越来越不想说,因为这世上人心凉薄,能让
我们交心的人寥寥无几。
有人说,生活的本质就是一个人活着,不要对别人心存太多期待,倘若没有过多的欢喜,并不会有极度的悲伤,
人只有寒心一次。才知道世界的真真假假,被最信任的人欺骗一次,才明白这世上没有一成不变的心,
有些话不再逢人就说,有些路,也不再期待有谁能同行哪些苦衷,背叛,不是没感觉而是知道说与不说都一样。
那些伤口不是不在乎,而是明白,除了咬牙硬撑,别无他法。
我们那些惊天动地的伤痛,在别人眼里不过是随手扶过的尘埃,慢慢的开始,不悲不痛,也不再期望什么。
逢人不必延申,孤独本事常态,倘若深情别辜负,余生孤独又何妨,懂我们的人不用解释,不懂我们的人,
百口莫辩。成年人的世界万般皆苦,只可自渡,一个人成熟的标志是学会了沉默,因为看淡了许多事,
看清了许多人。
愿你懂得沉默,做好眼前的事,内心充盈,从此不喧哗,不声张,有不动声色的脸。
—————— 一禅心灵庙语
文章目录
- Java:进一步理解多态性
- 每博一文案
- 1. 面向对象特征之一: 多态性
- 1.2 多态的重要性
- 2. 多态是:运行时类型 “编译看左边,运行看右边”
- 3. 重写
- 3.1 方法重写, 变量不重写
- 4. 多态的总结
- 5. 最后:
1. 面向对象特征之一: 多态性
多态性 是面向对象中最重要的概念,在Java中的体现:
-
对象的多态性: 父类的引用指向子类的对象 。其实多态性,可以理解为是 向上转型 ,只是大多是程序员,不说向上转型,而是说多态。
-
可以应用在 抽象类 和 接口 上。
-
Java引用变量有两个类型:编译时类型 和 运行时类型 。
- 编译时类型: 由声明该变量时使用的类型决定。
- 运行时类型: 由实际赋给该变量的对象决定,简称:编译时,看左边;运行时,看右边。
-
若编译时类型和运行时类型不一致,就出现了对象的多态性
-
多态情况下:看左边: 看父类的引用(父类中不具备子类特有的方法和变量);看右边: 看的是子类的对象 (实际运行的是子类重写父类的方法)。
1.2 多态的重要性
- 实现了代码的通用性
- JDBC : 使用Java程序操作(获取数据库连接,CRUD)数据库(MYSQL,Oracle,DB2,SQL Server)
- 抽象类,接口的使用体现了多态性:(抽象类,接口是不能
new
实例化的),所以我们只能调用其中的实现的抽象类,接口的 类 了,而如果没有 **多态性(向上转型)**这个特点,我们就无法调用 对应 抽象类,接口 实现的子类了。这样导致 抽象类,接口无法使用了。
2. 多态是:运行时类型 “编译看左边,运行看右边”
多态是运行时类型: 简单的说就是我们从编译上无法看出,所要指执行的结果,而是 运行 以后才能知道结果。
所以对于多态的执行的运行结果: “编译看左边,运行看右边”
如下代码:
package blogs.blog1;
public class PersonTest{
public static void main(String[] args) {
Person p1 = new Student(); // 多态: 父类的引用执行子类对象
p1.show(); // 执行的是 Student()子类重写的方法,
}
}
class Person { // 父类
String name;
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public void show() {
System.out.println("我是 Person");
}
}
class Student extends Person{ // 子类
int studentId;
@Override
public void show() {
System.out.println("我是 Student");
}
}
class Teacher extends Person{ // 子类
int teacherId;
@Override
public void show() {
System.out.println("我是 Teacher");
}
}
提出其中一部分,重要的代码分析:
public class PersonTest{
public static void main(String[] args) {
Person p1 = new Student(); // 多态: 父类的引用执行子类对象
p1.show(); // 执行的是 Student()子类重写的方法,
}
}
上述代码 Person p1 = new Student()
明明 编译时 赋值的对象是 Person 父类类型,可我们调用运行时 执行的却是其中子类 Student的 show() 方法。这就体现了,多态是运行时类型 。我们通过编译无法确定执行的结果。而是该对应的方法运行时 执行后,才可以确定结果(调用的是子类的方法)。简单的理解就是 :编译看左边,运行看右边 。
这句话对应上述代码就是:编译的时候,看左边:new Student()赋值给的是 Person 类型的,而实际运行的结果,看右边:运行的结果执行的是 Student 子类中的 show 方法。
我们再看如下代码:
public class PersonTest{
public static void main(String[] args) {
Person p1 = new Student(); // 多态: 父类的引用执行子类对象
p1.show(); // 执行的是 Student()子类重写的方法,
Person p2 = new Teacher(); // 多态:编译看左边,运行看右边
p2.show(); // 实际执行的是 Teacher() 子类重写的方法
}
}
正常情况的调用:
我们编译对象是什么类型的,我们就执行所对应对象下的方法,变量。如:编译的是 Person 类型的,执行的就是 Person的方法/变量。
public class PersonTest{
public static void main(String[] args) {
Person person = new Person();
person.show();
Student student = new Student();
student.show();
Teacher teacher = new Teacher();
teacher.show();
}
}
虚拟方法调用(多态的情况下)
子类中定义了 与 父类同名同参数 的方法,在多态情况下 ,将此时父类的方法称为 虚拟方法 ,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方式导致方法调用在 编译器是无法确定的。
只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
public class PersonTest{
public static void main(String[] args) {
Person p1 = new Student(); // 多态: 父类的引用执行子类对象
p1.show(); // 执行的是 Student()子类重写的方法,
Person p2 = new Teacher(); // 多态:编译看左边,运行看右边
p2.show(); // 实际执行的是 Teacher() 子类重写的方法
}
}
需要注意的点: 多态中,子类和父类都有的同名变量,是不会被重写的(调用的就不是子类中的变量了)。调用的仅仅只是编译赋值类型中的变量,在多态中:子类和父类同名的变量。编译时和运行时都是看左边.
如下代码:
package blogs.blog1;
public class PersonTest{
public static void main(String[] args) {
Person p1 = new Student();
System.out.println(p1.name); // 调用的是 父类 Person中的 name
Person p2 = new Teacher();
System.out.println(p2.name); // 同样调用的还是,父类 Person 中的 name 方法
}
}
class Person { // 父类
String name = "Person";
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public void show() {
System.out.println("我是 Person");
}
}
class Student extends Person{ // 子类
int studentId;
String name = "Student";
@Override
public void show() {
System.out.println("我是 Student");
}
}
class Teacher extends Person{ // 子类
int teacherId;
String name = "Teacher";
@Override
public void show() {
System.out.println("我是 Teacher");
}
}
public class PersonTest{
public static void main(String[] args) {
Person p1 = new Student();
System.out.println(p1.name); // 调用的是 父类 Person中的 name
Person p2 = new Teacher();
System.out.println(p2.name); // 同样调用的还是,父类 Person 中的 name 方法
}
}
注意: 在多态(向上转型),一个引用类型变量,如果声明为父类的类型,但实际引用的是子类对象,那么该变量是不能
再访问 子类中所特有的属性和方法
的。编译上是无法通过的。因为你左边赋值的是一个父类的类型,却想调用 父类中没有的属性和方法(就是子类中特有的属性和方法) 编译是无法通过的。虽然你无法调用访问子类中特有的属性和方法,但是对于 new 了子类对象,其中的属性和方法是会加载到内存当中的,无法访问不代表没有加载到内存当中,继承中父类中的 private 属性和方法,也是同样的,无法调用,不代表没有加载到内存当中。这是两回事。
如下代码
package blogs.blog1;
public class PersonTest{
public static void main(String[] args) {
Person p1 = new Student(); // 多态:父类的引用指向子类的对象
System.out.println(p1.studentId);// 调用子类特有的属性,编译无法通过
p1.ncee(); // 调用子类特有的方法,编译无法通过
// 因为我们左边编译赋值的是 Person 父类类型,该父类中没有子类特有的属性和方法,编译自然无法通过了。
// 编译都无法通过,更无法调用运行了。
}
}
class Person { // 父类
String name = "Person";
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public void show() {
System.out.println("我是 Person");
}
}
class Student extends Person{ // 子类
int studentId; // 该子类特有的属性
@Override
public void show() {
System.out.println("我是 Student");
}
// 该子类特有的方法
public void ncee() {
System.out.println("高考");
}
}
class Teacher extends Person{ // 子类
int teacherId; // 该子类特有的属性
@Override
public void show() {
System.out.println("我是 Teacher");
}
// 该子类特有的方法
public void teach() {
System.out.println("教书育人");
}
}
3. 重写
基于 继承,多态 在子类中可以根据需要对从 父类 中继承而来的方法进行改造。
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为 方法的重置,覆盖,重写 。在程序执行时,子类的方法将覆盖父类的方法。
重写的要求:
- 子类重写的方法必须和父类被重写的方法 具有相同的方法名以及参数列表
- 子类重写的方法返回值类型必须
<=
父类被重写的方法的返回值类型 - 子类重写的方法使用的访问权限必须
>=
父类被重写的方法的访问权限,特殊的 public 权限是最大的,父类声明了 public 子类就必须声明 public 。 - 子类不能重写 父类中声明为 private 权限的方法
- 子类方法抛出的 异常必须
<=
父类被重写方法的异常
注意点:
- 子类与父类中同名同参数的方法必须同时声明为非 static 的才可以重写,获取同时声明为 static 的(不是重写)。因为 static 方法时属于类的,子类无法覆盖父类的方法。
- 重写的是方法,对于变量是没有重写,这种说法的.
如下代码:
package blogs.blog1;
public class PersonTest{
public static void main(String[] args) {
Person p1 = new Student(); // 多态
p1.show(); // 调用的是 Student子类中重写的方法
Person p2 = new Teacher(); // 多态
p2.show(); // 调用的是 Teacher()子类重写的方法
}
}
class Person { // 父类
String name = "Person";
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public void show() {
System.out.println("我是 Person");
}
}
class Student extends Person{ // 子类
int studentId;
@Override
public void show() {
System.out.println("我是 Student");
}
public void ncee() {
System.out.println("高考");
}
}
class Teacher extends Person{ // 子类
int teacherId;
@Override
public void show() {
System.out.println("我是 Teacher");
}
public void teach() {
System.out.println("教书育人");
}
}
3.1 方法重写, 变量不重写
注意: 重写的对象是方法,变量是不会重写的,当父类/子类中都含有同名的变量,看左边编译赋值的是什么类型(子类/父类) 就调用那个类中定义的变量
如下代码:
package blogs.blog1;
public class PersonTest{
public static void main(String[] args) {
Person p1 = new Student();
System.out.println(p1.name); // 对于编译看左边,调用的是 Person中定义的 name的变量值
Person p2 = new Teacher();
System.out.println(p2.name); // 同理的,调用的是左边编译 Person 中定义的 name的变量值
}
}
class Person { // 父类
String name = "Person";
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public void show() {
System.out.println("我是 Person");
}
}
class Student extends Person{ // 子类
int studentId;
String name = "Student";
@Override
public void show() {
System.out.println("我是 Student");
}
public void ncee() {
System.out.println("高考");
}
}
class Teacher extends Person{ // 子类
int teacherId;
@Override
public void show() {
System.out.println("我是 Teacher");
}
public void teach() {
System.out.println("教书育人");
}
}
4. 多态的总结
- 多态是运行时类型, 编译看左边, 运行看右边
- 抽象类,接口(无法 new 实例对象) 的使用充分体现了多态性, 调用实现的子类中的方法.
- 多态中,父类/子类中都含有的同名的变量名, 无论是编译,还是运行的结果 都是看左边的编译赋值的是子类/父类 类型就调用谁中的变量值.因为变量是无法重写的。
- 重写中返回类型要必须
<=
父类中被重写的返回类型,重写中的声明的权限类型必须>=
父类中被重写的声明的权限特殊的(public,private) 。 - 方法才可以重写,变量无法重写。
5. 最后:
限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后悔有期。