JAVA基础(十一)

向下转型

有了对象的多态性以后,内存上实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用。

如何才能调用子类特有的属性和方法?

向下转型:使用强制类型转换符

注:此处完整代码见JAVA基础(十)

Person p1 = new Man();
Man m1 = (Man)p1;    

JAVA基础(十一)_第1张图片

 使用强转时,可能出现ClassCastException的异常

Person p1 = new Man();
Woman m1 = (Woman )p1;
m1.goShoping();    //异常

因为内存中没有Woman的属性和方法

instanceof 操作符

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方法显示对象的面积(注意方法的参 数类型,利用动态绑定技术)。

JAVA基础(十一)_第2张图片

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类的使用

Object类是所有Java类的根父类

如果在类的声明中未使用extends关键字指明其父类,则默认父类 为java.lang.Object类

无属性,只有一个空参构造器

常用方法:equals () 、toString () 、getClass () 、hashCode () 、clone () 、finalize () 、wait () 、

notify () 、notifyAll

==操作符与equals方法

= =:

        基本类型比较值:只要两个变量的值相等,即为true。

        引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回true。

用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则会报错

equals():

        所有类都继承了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重写了

重写equals方法

@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);
	}

重写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”。

==和equals的区别

== 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型 就是比较内存地址

equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也 是

==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中 用的比较多,久而久之,形成了equals是比较值的错误观点。

具体要看自定义类里有没有重写Object的equals方法来判断。

通常情况下,重写equals方法,会比较类中的相应属性是否都相等。

toString() 方法

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代表圆形。

JAVA基础(十一)_第3张图片

JAVA基础(十一)_第4张图片

 写一个测试类,创建两个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());
	}
}

单元测试(JUnit)

使用方法:

1、右键当前项目文件夹——Build Path——Add Libraries

JAVA基础(十一)_第5张图片

 选择JUnit——Next

JAVA基础(十一)_第6张图片

 选择JUnit4——Finish

JAVA基础(十一)_第7张图片

2、创建一个Java类进行单元测试 

要求:

        此时的Java类要求是Public的;

        此类提供Public的无参构造器

3、此类中声明一个单元测试方法:

要求:

        此方法权限是public的

        没有返回值

        没有形参

4、此单元测试方法上需要声明注解:@Test,并导入相关java包:org.junit.Test

5、声明号单元测试方法以后,就能在方法体内测试相关代码

6、写完代码以后,左键双击方法名,右键run as JUnitTest

 如果执行结果没有任何异常,为绿条

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

JAVA基础(十一)_第8张图片

测试

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());
	}
}

包装类(Wrapper)的使用

针对八种基本数据类型定义相应的引用类型—包装类(封装类)

有了类的特点,就可以调用类中的方法,Java才是真正的面向对象

JAVA基础(十一)_第9张图片

基本数据类型包装成包装类的实例——装箱

通过包装类的构造器实现:

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);

JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。

        //自动装箱:
		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;
}

 总结:基本类型、包装类与String类间的转换

JAVA基础(十一)_第10张图片

面试题

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);
		}
	}
}

你可能感兴趣的:(java,java)