多态可以理解为一个事物的多种形态。
对象的多态性:父类的引用指向子类的对象(或子类的对象赋值给父类的引用)
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
编译时,看左边;运行时,看右边。
例1:人、男人、女人
Person.java:
public class Person {
String name;
int age;
int id = 1001;
public void eat() {
System.out.println("人:吃饭");
}
public void walk() {
System.out.println("人:走路");
}
}
Man.java:
public class Man extends Person {
boolean isSmoking;
int id = 1002;
public void earnMoney() {
System.out.println("男人挣钱");
}
@Override
public void eat() {
System.out.println("男人吃肉,多长肉");
}
@Override
public void walk() {
System.out.println("男人走路带风");
}
}
Woman.java:
public class Woman extends Person {
boolean isBeauty;
public void goShopping() {
System.out.println("女人喜欢买买买");
}
@Override
public void eat() {
System.out.println("女人少吃为了减肥");
}
@Override
public void walk() {
System.out.println("女人走路的样子很好看");
}
}
PersonTest.java:
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
Man m1 = new Man();
m1.eat();
m1.earnMoney();
System.out.println("************************");
Person p2 = new Man();
//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
p2.eat();
p2.walk();
//对象的多态只适用于方法,不适用于属性
System.out.println(p2.id); //1001
}
}
例2:生动的多态例子
AnimalTest.java:
public class AnimalTest {
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.func(new Dog());
test.func(new Cat());
}
public void func(Animal animal) {
animal.eat();
animal.shut();
}
}
class Animal{
public void eat() {
System.out.println("动物:进食");
}
public void shut() {
System.out.println("动物:叫");
}
}
class Dog extends Animal{
public void eat() {
System.out.println("狗:吃肉和骨头");
}
public void shut() {
System.out.println("狗:汪!汪!汪!");
}
}
class Cat extends Animal{
public void eat() {
System.out.println("猫:吃鱼和老鼠");
}
public void shut() {
System.out.println("猫:喵!喵!喵!");
}
}
在多态情况下,子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
Person e = new Student();
e.getInfo();
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类 的getInfo()方法,也可以叫做动态绑定。
成员变量不具有多态性。
对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法, 这称为“早绑定”或“静态绑定”。
对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体 方法,这称为“晚绑定”或“动态绑定”。
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
可以通过向下转型的方法来解决这个问题。
在进行强制转换时,很可能产生ClassCastException异常,需要使用instanceof关键字来进行判断。
a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
如果 a instanceof A返回true,则 a instanceof B也返回true。其中类B是类A的父类。
//Person、Man、Woman见1.2的例1
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Man();
p1.name = "TOM";
//p1.isSmoking;
//p1.earnMoney();
//有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致
//编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
//向下转型:强制类型转化符
Man m1 = (Man)p1;
m1.isSmoking = true;
m1.earnMoney();
if(p1 instanceof Woman) {
Woman w1 = (Woman)p1;
w1.goShopping();
System.out.println("p1 为 Woman 的实例");
}
if(p1 instanceof Man) {
Man m2 = (Man)p1;
System.out.println("p1为Man的实例");
}
if(p1 instanceof Person) {
System.out.println("p1为Person的实例");
}
if(p1 instanceof Object) {
System.out.println("p1为Object的实例");
}
}
}
1.Object类是所有的 Java类的根父类
2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
3.Object中的属性和方法有通用性
注意:Object类中没有属性!
方法:clone()、equals()、finalize()、getClass()、hasCode()、notify() 、notifyAll() 、toString() 、wait()
1.可以使用在基本数据类型变量和引用数据类型变量中
2.如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
补充: == 符号使用时,必须保证符号左右两边的变量类型一致。
1.是一个方法,而非运算符
2. 只能适用于引用数据类型
3. Object类中equals()的定义:
public boolean equals(Object obj) {
return (this == obj);
}
说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
4. 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
5. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写.
重写的原则:比较两个对象的实体内容是否相同.
EqualsTest.java:
public class EqualsTest {
public static void main(String[] args) {
//基本数据类型
int i = 10;
int j = 10;
System.out.println(i == j); //true
double k = 10.0;
System.out.println(i == k); //true
char a1 = 'A';
char a2 = 65;
System.out.println(a1 == a2);//true
int a3 = 65;
double a4 = 65;
System.out.println(a1 == a3); //true
System.out.println(a2 == a4); //true
//引用数据类型
Customer cust1 = new Customer("Tom", 18);
Customer cust2 = new Customer("Tom", 18);
System.out.println(cust1 == cust2); //false
String s1 = new String("abcd");
String s2 = new String("abcd");
System.out.println(s1 == s2); //false
System.out.println("**********************");
System.out.println(cust1.equals(cust2));//false 可以通过重写来让变为true
System.out.println(s1.equals(s2));//true
}
}
Customer.java:
public class Customer {
private String name;
private int age;
public Customer() {
super();
}
public Customer(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//自动生成equals方法
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Customer other = (Customer) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
//重写原则:比较两个对象的实体内容是否相同(name、age)
//手动实现,存在漏洞!!!!
// @Override
// public boolean equals(Object obj) {
// if(this == obj) {
// return true;
// }
// if(obj instanceof Customer) {
// Customer cust = (Customer)obj;
// return this.age == cust.age && this.name.equals(cust.name);
// }else {
// return false;
// }
// }
}
1.toString()方法在Object类中定义,其返回值是String类型,返回类名和它 的引用地址。
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
2.像String、Date、File、包装类等都重写了Object类中的toString()方法。 使得在调用对象的toString()时,返回"实体内容"信息。
3.自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"
1.选中当前工程 - 右键选择:build path - add libraries - JUnit 4 - 下一步
2.创建Java类,进行单元测试。
此时的Java类要求:① 此类是public的 ②此类提供公共的无参的构造器。
3.此类中声明单元测试方法。
此时的单元测试方法:方法的权限是public,没有返回值,没有形参
4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test。
5.声明好单元测试方法以后,就可以在方法体内测试相关的代码。
6.写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Test
JUnitTest.java:
import org.junit.jupiter.api.Test;
public class JUnitTest {
@Test
public void testEquals() {
String s1 = "MM";
String s3 = "MM";
System.out.println(s1.equals(s3));
}
}
运行后的结果:
如果执行结果没有任何异常:绿条
如果执行结果出现异常:红条
装箱:基本数据类型包装成包装类实例
拆箱:获得包装类对象中包装的基本变量
JDK1.5之后,支持自动装箱,自动拆箱,前提是类型必须相互匹配。
import org.junit.jupiter.api.Test;
public class WrapperTest {
//基本数据类型->包装类 :调用包装类的构造器
@Test
public void test1() {
int num = 10;
Integer int1 = new Integer(num);
System.out.println(int1);
Integer int2 = new Integer("10");
System.out.println(int2);
Float f1 = new Float(12.3f);
Float f2 = new Float("12.3");
System.out.println(f1);
System.out.println(f2);
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean("TrUe");
Boolean b3 = new Boolean("TrUe123"); //Boolean若未赋值,初始化为null
System.out.println(b1); //true
System.out.println(b2); //true
System.out.println(b3); //false
}
//包装类->基本数据类型:调用包装类的Xxx的xxValue()方法
@Test
public void test2() {
Integer int1 = new Integer(12);
int a = int1.intValue();
System.out.println(a + 1);
Double b1 = new Double(10.25);
double b = b1.doubleValue();
System.out.println(2 * b);
}
//JDK 5.0 新特性:自动装箱与自动拆箱
@Test
public void test3() {
//自动装箱:基本数据类型->包装类
int num1 = 10;
Integer num2 = num1;//自动装箱
boolean b1 = true;
Boolean b2 = b1; //自动装箱
//自动拆箱:包装类->基本数据类型
Integer num3 = new Integer(100);
int num4 = num3;//自动拆箱
}
//基本数据类型、包装类——>String类型:调用String重载得valueOf()
@Test
public void test4() {
int num = 10;
//方式一:连接运算
String str1 = num + "";
//方式二:调用String的valueOf
float f1 = 1.23f;
String str2 = String.valueOf(f1);
System.out.println(str1);
System.out.println(str2);
}
//String类型——>基本数据类型、包装类:调用包装类的parseXxx(String s)
@Test
public void test5() {
String str = "123";
int num = Integer.parseInt(str);
System.out.println(num + 1);
String str1 = "true1";
boolean b1 = Boolean.parseBoolean(str1);
System.out.println(b1);//false 注意:返回不标准的true也是false
}
}
public void method1() {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//false
Integer m = 1;
Integer n = 1;
System.out.println(m == n);//true
Integer x = 128;
Integer y = 128;
System.out.println(x == y);//false
}
注意在Integer类中定义了内部类IntegerCache结构,在这个结构中定义了Integer[]保存了-128~127范围内的整数。如果我们使用自动装箱的方式,给Integer在-128至127之间赋值时可以直接使用数组中的元素,不用再去new一个新的元素。
public class InstanceTest {
public static void main(String[] args) {
InstanceTest t1 = new InstanceTest();
t1.method(new Person());
System.out.println();
t1.method(new Student());
System.out.println();
t1.method(new Graduate());
}
public void method(Person e) {
System.out.println(e.getInfo());
if(e instanceof Graduate) {
System.out.println("a graduate , student, person");
}else if(e instanceof Student) {
System.out.println("a student, person");
}else {
System.out.println("a person");
}
}
}
class Person{
protected String name="person";
protected int age=50;
public String getInfo() {
return "Name: "+ name + ", " +"age: "+ age;
}
}
class Student extends Person{
protected String school="pku";
public String getInfo() {
return "Name: "+ name + ", age: "+ age
+ ", school: "+ school;
}
}
class Graduate extends Student{
public String major="IT";
public String getInfo(){
return "Name: "+ name + ", age: "+ age
+ ", school: "+ school+", major:"+major;
}
}
public class GeometricObject {
String color;
double weight;
public GeometricObject(String color, double weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double findArea() {
return 0.0;
}
}
Circle.java:
public class Circle extends GeometricObject {
private double radius;
public Circle(String color, double weight, double radius) {
super(color, weight);
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public double findArea() {
return Math.PI*radius * radius;
}
}
MyRectangle.java:
public class MyRectangle extends GeometricObject{
private double width;
private double height;
public MyRectangle(double width, double height,String color, double weight) {
super(color, weight);
// TODO Auto-generated constructor stub
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public double findArea() {
return width * height;
}
}
GeometricTest.java:
public class GeometricTest {
public static void main(String[] args) {
GeometricObject g1 = new Circle("red",1,1);
GeometricObject g2 = new Circle("red",1,1);
GeometricObject g3 = new MyRectangle(2,3,"red",1);
GeometricObject g4 = new MyRectangle(3,2,"red",1);
GeometricTest t = new GeometricTest();
t.displayGeometricObject(g1);
t.displayGeometricObject(g2);
t.displayGeometricObject(g3);
t.displayGeometricObject(g4);
if(t.equalsArea(g3, g4)) {
System.out.println("g3 g4 面积相等");
}else {
System.out.println("g3 g4 面积不等");
}
if(t.equalsArea(g3, g2)) {
System.out.println("g3 g2 面积相等");
}else {
System.out.println("g3 g2 面积不等");
}
}
public boolean equalsArea(GeometricObject g1, GeometricObject g2) {
return g1.findArea() == g2.findArea();
}
public void displayGeometricObject(GeometricObject g) {
System.out.println("图形的面积为:" + g.findArea());
}
}
public class Order {
int orderId;
String orderName;
public Order() {
super();
}
public Order(int orderId, String orderName) {
super();
this.orderId = orderId;
this.orderName = orderName;
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj instanceof Order) {
Order ord = (Order)obj;
return this.orderId == ord.orderId && this.orderName.equals(ord.orderName);
}else {
return false;
}
}
}
OrderTest.java:
public class OrderTest {
public static void main(String[] args) {
Order o1 = new Order(1,"abc");
Order o2 = new Order(1,"abc");
System.out.println(o1.equals(o2));
}
}
参考资料:
[1]尚硅谷宋康红java基础教程