有了对象的多态性以后,内存上实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用。
如何才能调用子类特有的属性和方法?
向下转型:使用强制类型转换符
注:此处完整代码见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);
}
}
}