Java_多态
Posted 小企鹅推雪球!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java_多态相关的知识,希望对你有一定的参考价值。
文章目录
Java_多态
Java_多态的概念
- 多态是同一个行为具有多个不同表现形式或者形态的能力,举个例子,人都要上洗手间,但是有专门为男人准备的,也有为女人准备的
- 多态就是同一个接口,使用不同的实例而执行不同操作
多态的优点:消除类型之间的耦合关系,可替换性,可扩充性,多态使得继承使用更加灵活
多态存在的必要条件:
- 继承,重写,父类引用指向子类对象
Parent p = new Child();
对象的多态性就是指:父类的引用指向子类的对象
Java_使用多态时的注意事项
- 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
- 多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
- 多态性可以直接应用在抽象类和接口上
- Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定,简称:编译时,看左边;运行时,看右边。
- 若编译时类型和运行时类型不一致,就出现了对象的多态性
(Polymorphism)
- 多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法)
- 多态情况下,“看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
Java_对象的多态性
- 对象的多态 :在Java中,子类的对象可以替代父类的对象使用
- 一个变量只能有一种确定的数据类型
- 一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student();
m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”; //非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。
多态的实例
- 方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法
public class Test {
public void method(Person e) {
e.getInfo();
}
public static void main(Stirng args[]) {
Test t = new Test();
Student m = new Student();
t.method(m); // 子类的对象m传送给父类类型的参数e
}
}
Java_虚拟方法调用(Virtual Method Invocation)
正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚拟方法调用(多态情况下)
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
虚函数(虚拟方法)的概念
- 虚函数(虚拟方法)的存在是为了多态。
- 子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,
- 父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
- Java 中其实没有虚函数的概念,它的普通函数就相当于
C++
的虚函数,动态绑定是Java的默认行为。 - 如果 Java 中不希望某个函数具有虚函数特性,可以加上
final
关键字变成非虚函数。
编译时类型和运行时类型
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
- 编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。
Java_多态总结
- 多态的作用:提高了代码的通用性,常称作接口重用
- 多态的前提:需要存在继承或者实现关系,有方法的重写
- 编译时:要查看引用变量所声明的类中是否有所调用的方法。
- 运行时:调用实际new的对象所属的类中的重写方法。
- 成员变量:不具备多态性,只看引用变量所声明的类
Java_子类继承父类
继承成员变量和继承方法的区别
package com.company;
// 继承成员变量和继承方法的区别
public class Java_JiCheng_01 {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count); // 20
s.display(); // 20
Base b = s;
System.out.println(b == s); // true
System.out.println(b.count); // 10
b.display(); // 10
}
}
class Base {
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count = 20;
public void display() {
System.out.println(this.count); // 20
}
}
- 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
- 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,子类中的实例变量依然不可能覆盖父类中定义的实例变量
Java_instanceof 操作符
x instanceof A
:检验x是否为类A的对象,返回值为boolean型。 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。x instanceof A
: 如果x属于类A的子类B,x instanceof A值也为true。
Java_对象类型转换 (Casting )
基本数据类型的Casting:
- 自动类型转换:小的数据类型可以自动转换成大的数据类型,
如long g=20; double d=12.0f
- 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型,
如 float f=(float)12.0; int a=(int)1200L
对Java对象的强制类型转换称为造型
- 从子类到父类的类型转换可以自动进行
- 从父类到子类的类型转换必须通过造型(强制类型转换)实现
- 无继承关系的引用类型间的转换是非法的
- 在造型前可以使用instanceof操作符测试一个对象的类型
对象类型转换实例一
public class ConversionTest {
public static void main(String[] args) {
double d = 13.4;
long l = (long) d;
System.out.println(l);
int in = 5;
// boolean b = (boolean)in;
Object obj = "Hello";
String objStr = (String) obj;
System.out.println(objStr);
Object objPri = new Integer(5);
// 所以下面代码运行时引发ClassCastException异常
String str = (String) objPri;
}
}
对象类型转换实例二
public class Test {
public void method(Person e) { // 设Person类中没有getschool() 方法
// System.out.pritnln(e.getschool()); //非法,编译时错误
if (e instanceof Student) {
Student me = (Student) e; // 将e强制转换为Student类型
System.out.pritnln(me.getschool());
}
}
public static void main(String[] args){
Test t = new Test();
Student m = new Student();
t.method(m);
}
}
Java_Object 类
- Object类是所有Java类的根父类,如果在类的声明中未使用
extends
关键字指明其父类,则默认父类为java.lang.Objec
t类 public class Person
等价于:public class Person extends Object
Java_== 和equals 方法
==操作符
==
:基本类型比较值:只要两个变量的值相等,即为true
。int a=5; if(a==6){…}
- 引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,
==
才返回true
。
Person p1=new Person();
Person p2=new Person();
if (p1==p2){…}
- 用
“==”
进行比较时,符号两边的数据类型必须兼容
(可自动转换的基本数据类型除外),否则编译出错
equals方法
equals()
:所有类都继承了Object,也就获得了equals()
方法。还可以重写。- 只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。格式:
obj1.equals(obj2)
- 特例:当用
equals()
方法进行比较时,对类File、String、Date及包装类(Wrapper Class)
来说,是比较类型及内容而不考虑引用的是否是同一个对象;原因是:在File、String、Date及包装类(Wrapper Class)
类中重写了Object类的equals()
方法。 - 当自定义使用
equals()
时,可以重写。用于比较两个对象的“内容”是否都相等
Java_重写equals()方法的原则
- 对称性:如果
x.equals(y)
返回是“true”
,那么y.equals(x)
也应该返回是“true”
。 - 自反性:
x.equals(x)
必须返回是“true”
- 传递性:如果
x.equals(y)
返回是“true”
,而且y.equals(z)
返回是“true”
,那么z.equals(x)
也应该返回是“true”
。 - 一致性:如果
x.equals(y)
返回是“true”
,只要x和y内容一直不变,不管你重复x.equals(y)
多少次,返回都是“true”
。 - 任何情况下,
x.equals(null)
,永远返回是“false”
;x.equals
(和x不同类型的对象)永远返回是“false”
。
Java_==操作符和equals的区别
==
既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址equals
的话,它是属于java.lang.Object
类里面的方法,如果该方法没有被重写过默认也是==
;我们可以看到String
等类的equals
方法是被重写过的,而且String
类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。- 通常情况下,重写
equals
方法,会比较类中的相应属性是否都相等。
Java_toString()方法
toString()
方法在Object
类中定义,其返回值是String
类型,返回类名和它的引用地址。- toString()方法在Object类中定义,其返回值是String类型,返回类名和它
的引用地址。
Date now=new Date();
System.out.println(“now=”+now); 相当于
System.out.println(“now=”+now.toString());
- 可以根据需要在用户自定义类型中重写toString()方法
如String 类重写了toString()方法,返回字符串的值。
s1=“hello”;
System.out.println(s1);//相当于System.out.println(s1.toString());
- 基本类型数据转换为String类型时,调用了对应包装类的toString()方法
int a=10; System.out.println(“a=”+a)
Java_包装类(Wrapper)的使用
- Java针对八种基本数据类型定义相应的引用类型—包装类(封装类)
- 有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
基本数据类型包装成包装类的实例 —装箱
- 装箱通过包装类的构造器实现:
int i = 500; Integer t = new Integer(i);
- 装箱:还可以通过字符串参数构造包装类对象:
Float f = new Float(“4.56”);long l = new Long(“asdf”); //NumberFormatException
获得包装类对象中包装的基本类型变量 —拆箱
- 调用包装类的
.xxxValue()方法:boolean b = bObj.booleanValue();
JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配
字符串转换成基本数据类型:
- 通过包装类的构造器实现:
int i = new Integer(“12”)
- 通过包装类的
parseXxx(String s)
静态方法Float f = Float.parseFloat(“12.1”);
基本数据类型转换成字符串
- 调用字符串重载的valueOf()方法:
String fstr = String.valueOf(2.34f);
- 自动转换:
String intStr = 5 + ""
Java_包装类实例
装箱:包装类使得一个基本数据类型的数据变成了类。有了类的特点,可以调用类中的方法。
String s = t.toString(); // s = “500“,t是类,有toString方法
String s1 = Integer.toString(314); // s1= “314“ 将数字转换成字符串。
String s2=“4.56”;
double ds=Double.parseDouble(s2); //将字符串转换成数字
拆箱:将数字包装类中内容变为基本数据类型。
int j = t.intValue(); // j = 500,intValue取出包装类中的数据
// 包装类在实际开发中用的最多的在于字符串变为基本数据类型。
String str1 = "30" ;
String str2 = "30.3" ;
int x = Integer.parseInt(str1) ; // 将字符串变为int型
float f = Float.parseFloat(str2) ; // 将字符串变为int型
以上是关于Java_多态的主要内容,如果未能解决你的问题,请参考以下文章