有了对象的多态性以后,内存上实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用。
如何才能调用子类特有的属性和方法?
向下转型:使用强制类型转换符
注:此处完整代码见JAVA基础(十)
- Person p1 = new Man();
- Man m1 = (Man)p1;

使用强转时,可能出现ClassCastException的异常
- Person p1 = new Man();
- Woman m1 = (Woman )p1;
- m1.goShoping(); //异常
因为内存中没有Woman的属性和方法
x instanceof A:检验x是否为类A的实例,返回值为boolean型
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果x属于类A的子类B,x instanceof A值也为true。
- if (p1 instanceof Person) {
- System.out.println("____Person____");
- }
-
- if (p1 instanceof Object) {
- System.out.println("____Object____");
- }
为了避免在向下转型时出现异常,我们在向下转型前,先进行instanceof的判断,一旦返回true,就进行向下转型,否则就不执行向下转型
- if (p1 instanceof Woman) {
- System.out.println("____Woman____");
- Woman w2 = (Woman)p1;
- w2.goShopping();
- }
-
- if (p1 instanceof Man) {
- System.out.println("____Man____");
- Man m2 = (Man)p1;
- m2.earnMoney();
- }
测试代码
- package com.xxx.java;
-
- public class PersonTest {
- public static void main(String[] args) {
- Person p = new Person();
- p.eat();
-
- Man m = new Man();
- m.eat();
- m.age = 25;
- m.earnMoney();
-
- System.out.println("*******************");
- //多态性
- Person p1 = new Man();
-
- Person p2 = new Woman();
-
- p1.eat(); //执行子类重写的方法
- p1.walk();
- System.out.println(p1.id);
- //p1.earnMoney();
- System.out.println("**********************");
-
- //Man m1 = (Man)p1;
- //m1.earnMoney();
- //m1.isSomking = true;
-
- //Woman w1 = (Woman)p1;
- //w1.goShopping();
-
- if (p1 instanceof Woman) {
- System.out.println("____Woman____");
- Woman w2 = (Woman)p1;
- w2.goShopping();
- }
-
- if (p1 instanceof Man) {
- System.out.println("____Man____");
- Man m2 = (Man)p1;
- m2.earnMoney();
- }
-
- if (p1 instanceof Person) {
- System.out.println("____Person____");
- }
-
- if (p1 instanceof Object) {
- System.out.println("____Object____");
- }
-
- //练习:编译通过,运行不通过:
- //Person p3 = new Person();
- //Man m3 = (Man)p3;
-
- //编译通过,运行通过
- Object obj = new Woman();
- Person p4 =(Person)obj;
-
- //编译不通过
- //Man m5 = new Woman();
- }
- }
练习
- package com.xxx.exer;
-
- public class FieldMethodTest {
- 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(); //20
- }
- }
-
- 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);
- }
- }
子类继承父类
若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统 将不可能把父类里的方法转移到子类中。
对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实 例变量依然不可能覆盖父类中定义的实例变量
练习2
- class Person {
- protected String name="person";
- protected int age=50;
-
- public String getInfo() {
- return "Name: "+ name + "\n" +"age: "+ age;
- }
- }
-
- class Student extends Person {
- protected String school="pku";
-
- public String getInfo() {
- return "Name: "+ name + "\nage: "+ age + "\nschool: "+ school;
- }
- }
-
- class Graduate extends Student{
- public String major="IT";
-
- public String getInfo() {
- return "Name: "+ name + "\nage: "+ age + "\nschool: "+
- school+"\nmajor:"+major;
- }
- }
建立InstanceTest 类,在类中定义方法 method(Person e);
在method中:
(1)根据e的类型调用相应类的getInfo()方法。
(2)根据e的类型执行:
如果e为Person类的对象,输出: “a person”;
如果e为Student类的对象,输出: “a student”
“a person ”
如果e为Graduate类的对象,输出: “a graduated student”
“a student”
“a person”
- package com.xxx.exer;
-
- public class InstanceTest {
- public static void main(String[] args) {
- InstanceTest test = new InstanceTest();
- test.methed(new Person());
- }
-
- public void methed(Person e) {
- System.out.println(e.getInfo());
-
- if (e instanceof Graduate) {
- System.out.println("“a graduated student”");
- }
- if (e instanceof Student) {
- System.out.println("“a student”");
- }
- System.out.println("“a person”");
- }
- }
定义三个类,父类GeometricObject代表几何形状,子类Circle代表圆形,MyRectangle代表矩形。定义一个测试类GeometricTest,编写equalsArea方法测试两个对象的面积是否相等(注意方法的参 数类型,利用动态绑定技术),编写displayGeometricObject方法显示对象的面积(注意方法的参 数类型,利用动态绑定技术)。

- package com.xxx.exer1;
-
- public class GeometricObject {
- protected String color;
- protected double weight;
-
- public GeometricObject(String color, double weight) {
- super();
- 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;
- }
- }
- package com.xxx.exer1;
-
- 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 3.14 * radius * radius;
- }
- }
- package com.xxx.exer1;
-
- public class MyRectangle extends GeometricObject{
- private double width;
- private double height;
-
- public MyRectangle(String color, double weight, double width, double height) {
- super(color, weight);
- 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;
- }
- }
测试
- package com.xxx.exer1;
-
- public class GeometricTest {
-
- public static void main(String[] args) {
- GeometricTest test = new GeometricTest();
-
- Circle c1 = new Circle("white",1.0,2.3);
- test.displayGeometricObject(c1);
-
- Circle c2 = new Circle("white",1.0,3.3);
- test.displayGeometricObject(c2);
-
- System.out.println("c1和c2的面积是否相等:" + test.equalsArea(c1, c2));
-
- MyRectangle m1 = new MyRectangle("black",2.0,2.1,3.4);
- test.displayGeometricObject(m1);
- }
-
- public boolean equalsArea(GeometricObject o1,GeometricObject o2) {
- return o1.findArea() == o2.findArea();
- }
-
- public void displayGeometricObject(GeometricObject o) {
- System.out.println("面积为:" + o.findArea());
- }
- }
多态是编译时行为还是运行时行为? 如何证明?
- package com.xxx.exer1;
-
- import java.util.Random;
-
- //面试题:多态是编译时行为还是运行时行为?
- //证明如下:
- class Animal {
-
- protected void eat() {
- System.out.println("animal eat food");
- }
- }
-
- class Cat extends Animal {
-
- protected void eat() {
- System.out.println("cat eat fish");
- }
- }
-
- class Dog extends Animal {
-
- public void eat() {
- System.out.println("Dog eat bone");
-
- }
-
- }
-
- class Sheep extends Animal {
-
-
- public void eat() {
- System.out.println("Sheep eat grass");
-
- }
-
-
- }
-
- public class InterviewTest {
-
- public static Animal getInstance(int key) {
- switch (key) {
- case 0:
- return new Cat ();
- case 1:
- return new Dog ();
- default:
- return new Sheep ();
- }
-
- }
-
- public static void main(String[] args) {
- int key = new Random().nextInt(3);
-
- System.out.println(key);
-
- Animal animal = getInstance(key);
-
- animal.eat();
-
- }
-
- }
拓展问题:
- package com.xxx.exer1;
- //考查多态的笔试题目:
- public class InterviewTest1 {
-
- public static void main(String[] args) {
- Base base = new Sub();
- base.add(1, 2, 3);
-
- // Sub s = (Sub)base;
- // s.add(1,2,3);
- }
- }
-
- class Base {
- public void add(int a, int... arr) {
- System.out.println("base");
- }
- }
-
- class Sub extends Base {
-
- public void add(int a, int[] arr) {
- System.out.println("sub_1");
- }
-
- // public void add(int a, int b, int c) {
- // System.out.println("sub_2");
- // }
-
- }
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类 为java.lang.Object类
无属性,只有一个空参构造器
常用方法:equals () 、toString () 、getClass () 、hashCode () 、clone () 、finalize () 、wait () 、
notify () 、notifyAll
基本类型比较值:只要两个变量的值相等,即为true。
引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回true。
用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则会报错
所有类都继承了Object,也就获得了equals()方法。还可以重写。
只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。
源码:
- public boolean equals(Object obj) {
- return (this == obj);
- }
特例:当用equals()方法进行比较时,对类File、String、Date及包装类 (Wrapper Class ) 来说,是比较类型及内容而不考虑引用的是否是同一个对象
原因:在这些类中重写了Object类的equals()方法
- //比较基本数据类型
- int i = 10;
- int j = 10;
- double d = 10.0;
- System.out.println(i == j); //true
- System.out.println(i == d); //true
-
- boolean b = true;
- //System.out.println(i == b); 报错
-
- char c = 10;
- System.out.println(i == c); //true
-
- char c1 = 'A';
- char c2 = 65;
- System.out.println(c1 == c2);
-
- //比较引用数据类型
- Customer cust1 = new Customer("Tom",21);
- Customer cust2 = new Customer("Tom",21);
- System.out.println(cust1 == cust2);
-
- String str1 = new String("abc");
- String str2 = new String("abc");
- System.out.println(str1 == str2);
-
- System.out.println("*************************");
-
- //equals的使用
- System.out.println(cust1.equals(cust2));
- System.out.println(str1.equals(str2)); //String重写了
- @Override
- //比较两个对象的name和age是否相同
- 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);
-
- }
- return false;
-
- }
也可以使用Eclipse提供的一键生成equals方法
- //自动生成的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;
- return age == other.age && Objects.equals(name, other.name);
- }
对称性:如果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”。
== 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型 就是比较内存地址
equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也 是
==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中 用的比较多,久而久之,形成了equals是比较值的错误观点。
具体要看自定义类里有没有重写Object的equals方法来判断。
通常情况下,重写equals方法,会比较类中的相应属性是否都相等。
toString()方法在Object类中定义,其返回值是String类型,返回类名和它的引用地址。
当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
因为print方法若输出一个对象会调用valueOf方法,而valueOf方法只要该对象不为空就调用 toString方法
Object类中的源码
- public String toString() {
- return getClass().getName() + "@" + Integer.toHexString(hashCode());
- }
像String Date File 包装类 等 都重写了Object类的toString方法
自定义类也可以重写toString方法
- public String toString() {
- return "Customer[name = " + name + " age = " + age + "]";
- }
也可以自动生成:
- @Override
- public String toString() {
- return "Customer [name=" + name + ", age=" + age + "]";
- }
测试
- Customer cust1 = new Customer("Tom",21);
- System.out.println(cust1.toString());//输出地址值
- System.out.println(cust1); //输出地址值
-
- String str = new String("MM");
- System.out.println(str);
练习
定义两个类,父类GeometricObject代表几何形状,子类Circle代表圆形。


写一个测试类,创建两个Circle对象,判断其颜色是否相等;利用equals方法判断其半径是否 相等;利用 toString()方法输出其半径。
- package com.xxx.exer3;
-
- public class GeometricObject {
- protected String color;
- protected double weight;
-
- public GeometricObject() {
- super();
- this.color = "white";
- this.weight = 1.0;
- }
-
- public GeometricObject(String color, double weight) {
- super();
- 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;
- }
-
-
- }
- package com.xxx.exer3;
-
- public class Circle extends GeometricObject {
- private double radius;
-
- public Circle() {
- super();
- radius = 1.0;
- }
-
- public Circle(double radius) {
- super();
- this.radius = radius;
- }
-
-
- public Circle(double radius,String color,double weight) {
- super(color,weight);
- this.radius = radius;
- }
-
- public double getRadius() {
- return radius;
- }
-
- public void setRadius(double radius) {
- this.radius = radius;
- }
-
- public double findArea() {
- return 3.14 * radius * radius;
- }
-
- @Override
- public boolean equals(Object obj) {
- if(this == obj) {
- return true;
- }
- if(obj instanceof Circle) {
- Circle c = (Circle)obj;
- return this.radius == c.radius;
- }
- return false;
- }
-
- @Override
- public String toString() {
- return "Circle [radius=" + radius + "]";
- }
-
-
- }
- package com.xxx.exer3;
-
- public class CircleTest {
- public static void main(String[] args) {
- Circle c1 = new Circle(2.3);
- Circle c2 = new Circle(2.3, "white", 2.0);
-
- System.out.println("颜色是否相同:" + c1.getColor().equals(c2.getColor()));
-
- System.out.println("半径是否相同:" + c1.equals(c2));
-
- System.out.println(c1); //调用toString方法
- System.out.println(c2.toString());
- }
- }
使用方法:
1、右键当前项目文件夹——Build Path——Add Libraries

选择JUnit——Next

选择JUnit4——Finish

2、创建一个Java类进行单元测试
要求:
此时的Java类要求是Public的;
此类提供Public的无参构造器
3、此类中声明一个单元测试方法:
要求:
此方法权限是public的
没有返回值
没有形参
4、此单元测试方法上需要声明注解:@Test,并导入相关java包:org.junit.Test
5、声明号单元测试方法以后,就能在方法体内测试相关代码
6、写完代码以后,左键双击方法名,右键run as JUnitTest
![]()
如果执行结果没有任何异常,为绿条

如果执行结果出现异常,红条

测试
- package com.xxx.java2;
-
- import java.sql.Date;
-
- import org.junit.Test;
-
- public class JUnitTest {
- int num;
-
- public void show() {
- num = 20;
- System.out.println("show()");
- }
-
- @Test
- public void testEquals() {
- String s1 = "A";
- String s2 = "A";
- System.out.println(s1.equals(s2));
-
- Object obj = new String();
- Date date = (Date)obj;
-
- System.out.println(num);
- show();
- }
-
- @Test
- public void testToString() {
- String s2 = "A";
- System.out.println(s2.toString());
- }
- }
针对八种基本数据类型定义相应的引用类型—包装类(封装类)
有了类的特点,就可以调用类中的方法,Java才是真正的面向对象

通过包装类的构造器实现:
- int i = 500;
- Integer t = new Integer(i);
还可以通过字符串参数构造包装类对象:
- Float f = new Float(“4.56”);
- Long l = new Long(“asdf”); //NumberFormatException
调用包装类的.xxxValue()方法:
- Integer in1 = new Integer(12);
- int i1 = in1.intValue();
- System.out.println(i1 + 1);
- //自动装箱:
- int num2 = 10;
- Integer in1 = num2;
- //自动拆箱
- int num3 = in1;
- float f1 = 12.3f;
- String str2 = String.valueOf(f1);
-
- Double d1 = new Double(12.4);
- String str3 = String.valueOf(d1);
除此之外基本数据类型还可以通过字符串拼接来转换
- int num1 = 10;
- String str1 = num1 + "";
通过包装类的构造器实现:
- String str = "123"
- int i = new Integer(str);
通过包装类的parseXxx(String s)静态方法:
- String str2 = "true";
- boolean b1 = Boolean.parseBoolean(str2);
- System.out.println(b1);
- package com.xxx.java2;
-
- import org.junit.Test;
-
- public class WrapperTest {
- @Test
- public void test1() {
- // 基本数据类型——>包装类:
- int num = 10;
- Integer in1 = new Integer(num); // 传入int
- System.out.println(in1.toString());
-
- Integer in2 = new Integer("123"); // 传入String
- System.out.println(in2.toString());
-
- // 报异常
- // Integer in3 = new Integer("123abc"); //不能传入非数字
- // System.out.println(in3.toString());
-
- Float f1 = new Float(12.3f); // 传入float
- Float f2 = new Float(12.3f); // 传入double
- Float f3 = new Float("12.3"); // 传入String
- System.out.println(f1);
- System.out.println(f2);
- System.out.println(f3);
-
- Boolean b1 = new Boolean(true);
- Boolean b2 = new Boolean("true");
-
- // 不会报错
- Boolean b3 = new Boolean("tRuE");
- Boolean b4 = new Boolean("true123"); // 只有传入的字符串不是ture(忽略大小写都为false)
-
- Order order = new Order();
- System.out.println(order.isMale); // false
- System.out.println(order.isFemale); // null
- }
-
- // 包装类转换为基本数据类型
- @Test
- public void test2() {
- Integer in1 = new Integer(12);
- int i1 = in1.intValue();
- System.out.println(i1 + 1);
-
- Float f1 = new Float(12.3);
- float f2 = f1.floatValue();
- System.out.println(f2 + 1);
- }
-
- @Test
- public void test3() {
- // 包装类的使用
- int num1 = 10;
- // Integer in = new Integer(num1);
- // method(in);
- // JDK5.0新增自动装箱与拆箱
- method(num1);
-
- // 自动装箱:
- int num2 = 10;
- Integer in1 = num2;
-
- boolean b1 = true;
- Boolean b2 = b1;
-
- // 自动拆箱
- System.out.println(in1.toString());
- int num3 = in1;
- }
-
- public void method(Object obj) {
- System.out.println(obj);
- }
-
- @Test
- public void test4() {
- // 基本数据类型、包装类——>String类型
- // 方式1:
- int num1 = 10;
- String str1 = num1 + "";
- // 方式2:
- float f1 = 12.3f;
- String str2 = String.valueOf(f1);
-
- Double d1 = new Double(12.4);
- String str3 = String.valueOf(d1);
- System.out.println(str2);
- System.out.println(str3);
- }
-
- @Test
- public void test5() {
- // String类型——>基本数据类型、包装类
- String str1 = "123";
-
- //错误情况
- //int num1 = (int)str1;
- //Integer in = (Integer)str1;
-
- //调用包装类的parsexxx方法
- int num2 = Integer.parseInt(str1);
- System.out.println(num2 + 1);
-
- String str2 = "true";
- boolean b1 = Boolean.parseBoolean(str2);
- System.out.println(b1);
- }
- }
-
- class Order {
- boolean isMale;
- Boolean isFemale;
- }

1
- Object o1 = true ? new Integer(1) : new Double(2.0);
- System.out.println(o1); //1.0
解析:
三元运算符自动类型转换
多态虚拟方法调用
2
- Object o2;
- if (true)
- o2 = new Integer(1);
- else
- o2 = new Double(2.0);
- System.out.println(o2); //1
3
- 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
解析:
-128到127为常用数,在Integer内定义了一个IntegerCathe,里面定义了一个Intefer[ ],保 存了这个范围的整数,如有需要使用该数组的元素,不需要new,提高了效率,所以m == n
而128不在这个范围,需要在new一个新的对象,地址不同自然返回false
练习:
利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出 最高分,并输出学生成绩等级。
提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的 长度。而向量 类java.util.Vector可以根据需要动态伸缩。
创建Vector对象:Vector v=new Vector();
给向量添加元素:v.addElement(Object obj); //obj必须是对象
取出向量中的元素:Object obj=v.elementAt(0);
注意第一个元素的下标是0,返回值是Object类型的。
计算向量的长度:v.size();
若与最高分相差10分内:A等;20分内:B等; 30分内:C等;其它:D等
- package com.xxx.exer4;
-
- import java.util.Scanner;
- import java.util.Vector;
-
- public class ScoreTest {
- public static void main(String[] args) {
- Scanner scan = new Scanner(System.in);
-
- Vector v = new Vector();
- int maxScore = 0;
- for (;;) {
- System.out.println("请输入学生成绩(以负数代表输入结束)");
- int score = scan.nextInt();
- if (score < 0) {
- break;
- }
- if (score > 100) {
- System.out.println("输入的数据非法,请重新输入");
- continue;
- }
- v.addElement(score);
- if (maxScore < score) {
- maxScore = score;
- }
- }
- char level;
- for (int i = 0; i < v.size(); i++) {
- Object obj = v.elementAt(i);
-
- int score = (int)obj;
- if (maxScore - score <= 10) {
- level = 'A';
- } else if(maxScore - score <= 20){
- level = 'B';
- }else if(maxScore - score <= 30){
- level = 'C';
- }else {
- level = 'D';
- }
-
- System.out.println("student——" + i + ",score is " + score + ",level is " + level);
- }
- }
- }