文章目录
面向对象编程(中)
1、继承性的使用和理解
面向对象的特征之二: 继承性
一、为什么需要继承?
多个类存在相同的行为和属性,将这些内容抽取到单独一个类中,那么其他类无需再定义这些属性和行为, 只要继承这个类即可。
继承的好处?
①.减少代码的冗余,提高代码的复用性;
②.便于功能的拓展 (子类可以自定义自己特有的属性和方法)
③.为之后多态的使用,提供了前提。
二、继承性的格式
class A extends B{}
A:子类、派生类、subclass
B:父类、超类、基类、superclass
2.1 体现:一旦子类 A 继承父类以后,子类 A 中就获取了父类 B 中声明的结构:属性、方法
特别的,父类中声明为 private 的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。
只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
2.2 子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的拓展。
子类和父类的关系:不同于子集与集合的关系。
extends:延展、扩展
代码说明:
Person类:
package com.rucoding.day11;
/**
* @author LiuYiXin
*
*/
public class Person {
// 描述个人信息的person类
String name;
private int age;
// 无参构造器
public Person() {
}
//有参构造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 对私有化的属性提供对外得set和get方法
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
//
public void eat() {
System.out.println("吃饭");
sleep(); //调用睡觉的方法
}
public void sleep() {
System.out.println("睡觉");
}
}
Student :
package com.rucoding.day11;
/**
* @author LiuYiXin
*
*/
public class Student extends Person{
//子类继承了父类Person,则继承了其属性和方法
String major;
//无参构造器
public Student(){
}
//构造器
public Student(String name,int age,String major){
this.name = name;
// this.age = age;
setAge(age);
this.major = major;
}
public void study(){
System.out.println("学习");
}
public void show(){
System.out.println("name:" + name + ",age = " + getAge());
}
}
测试类:
package com.rucoding.day11;
/**
* @author LiuYiXin
*
*/
public class PersonStudentExtendsTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
System.out.println("==================");
Student s1 = new Student();
//Student类继承了父类的属性和方法
s1.name = "Tom"; //继承了父类的name属性
// s1.age = 10;//The field Person.age is not visible (父类私有化的属性子类不可以直接访问) 可见性大小,但是堆空间是有划分age属性的
s1.setAge(10); //继承父类Person的setAge方法,私有化的属性不可以直接访问,通过公共方法访问
System.out.println(s1.getAge());
}
}
图解继承关系:
多个class类拥有共同的属性和行为(方法),可抽取一个公共类,其他多个类从公共类获取,即可继承于它,从而拥有对应的属性和行为(方法)。
修改:
Creature类:
package com.rucoding.day11;
/**
* @author LiuYiXin
*
*/
public class Creature {
//定义方法
public void brease(){
System.out.println("呼吸");
}
}
Person类 继承于Creature类:
public class Person extends Creature { //继承于上述类Creature
...
}
测试类:
package com.rucoding.day11;
/**
* @author LiuYiXin
*
*/
public class ExtendsTest01 {
//
public static void main(String[] args) {
//
Student s1 = new Student();
/*
* ①.Student学生类 继承于父类Person类,Person类继承于父类Creature类,
* 此时Student拥有上述两个父类的属性和方法
* ②.对于Student类来说, Person类就是其直接父类, Creature类是简介父类
*/
s1.brease();
Creature c1 = new Creature();
System.out.println(c1.toString()); //c1. 此时会出现很多方法, 可是我们明明只是定义一个brease()方法
// 估计有的小伙伴执行c1.时弹出的方法, 注意到是Object类下的方法
}
}
小结:
1)、一个类可以被多继承
2)、java中类的单继承性: 一个类只能有一个父类
3)、子类直接继承的父类,称之为直接父类,间接继承的父类,称为间接父类。
4)、如果没有显式的定义父类,则该类继承于 java.lang.Object类,意味着,所有java类都具有 java.lang.Object类声明的方法。
练习1:
代码演示:
package com.rucoding.day11;
/**
* @author LiuYiXin
*
*/
public class CircleTest {
public static void main(String[] args) {
Cylinder cylinder = new Cylinder();
cylinder.setRaduis(2.1); //设置半径属性
cylinder.setLength(3.4);
double area = cylinder.findArea();
System.out.println("圆的面积为:" + area);
double volume = cylinder.findVolume();
System.out.println("圆的体积为:" + volume);
}
}
class Circle {
// 定义私有化属性
private double radius;
//
public Circle() {
this.radius = 2.0;
}
/**
* @return the radius
*/
public double getRadius() {
return radius;
}
/**
* @param raduis
* the radius to set
*/
public void setRaduis(double radius) {
this.radius = radius;
}
// 返回圆的面积
public double findArea() {
return Math.PI * radius * radius;
}
}
//
class Cylinder extends Circle {
private double length;
// 初始化构造器
public Cylinder() {
this.length = 2.0;
}
/**
* @return the length
*/
public double getLength() {
return length;
}
/**
* @param length
* the length to set
*/
public void setLength(double length) {
this.length = length;
}
// 计算体积
public double findVolume() {
return findArea() * length;
}
}
2、方法的重写(override/overwrite)
方法重写的概念:
重写: 子类继承父类以后,可以对父类的方法进行覆盖操作
应用:重写以后, 当创建子类的对象以后, 子类的对象调用与父类同名同参数的方法时,执行的是子类重写父类的方法,即是在程序执行的时候,子类的方法覆盖了父类的方法。
测试代码:
定义Person类:
package com.rucoding.day11_1;
/**
* @author LiuYiXin
*
*/
public class Person {
//设置属性
String name;
int age;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
/**
*
*/
public Person() {
super();
}
/**
* @param name
* @param age
*/
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("eating....");
}
//
public void walk(int distance){
System.out.println("走路的距离是: " + distance + "公里");
}
private void show(){
System.out.println("我是一个人的信息...");
}
public Object info(){
return null;
}
public double info1(){
return 1.0;
}
void privilegeTest(){
System.out.println("父类的权限修饰符缺省default....");
}
}
Student类:
package com.rucoding.day11_1;
/**
* @author LiuYiXin
*
*/
public class Student extends Person {
String major;
/**
*
*/
public Student(){}
public void study(){
System.out.println("学习的专业是:" + major);
}
/**
* @param major
*/
public Student(String major) {
super();
this.major = major;
}
//对继承的父类的方法进行重写 按住Alt / 快捷键即可实现
/* (non-Javadoc)
* @see com.rucoding.day11_1.Person#eat()
*/
@Override
public void eat() { //要点1: 父类被重写的方法返回值为void,子类必须是void类型
// TODO Auto-generated method stub
super.eat(); //调用父类的方法
System.out.println("学生学习任务重,可以多吃点有营养的..."); //自定义内容输出
}
//构成重写嘛?父类定义的show方法的权限修饰符为private的
//如果父类方法是private权限修饰符,则子类不可以重写
public void show(){
System.out.println("我是一个学生");
}
// 构成重写嘛?
//父类的info方法返回值是Object类型,构成重写方法,String类是Object类的子类
//要点2:父类被重写的方法返回值是A类型, 则子类重写的方法的返回值可以是A类型或者A类的子类的。
@Override
public String info(){
return null;
}
//父类的info1方法是基本数据类型的double,子类必须是一致的double类型
//要点3:父类被重写的方法返回值类型如果是基本数据类型(比如double),
//则子类被重写的方法返回值类型必须是相同的基本数据类型(必须是double)
// public int info1(){
// return 1;
// }
/* (non-Javadoc)
* @see com.rucoding.day11_1.Person#walk(int)
*/
@Override
public void walk(int distance) {
// TODO Auto-generated method stub
// super.walk(distance);
System.out.println("学生每日步行的距离是:" + distance);
}
/* (non-Javadoc)
* @see com.rucoding.day11_1.Person#privilegeTest()
*/
@Override
private void privilegeTest() { //要点4: 意味着,子类重写父类的方法的权限修饰符不小于父类的(即是大于等于关系)
//Cannot reduce the visibility of the inherited method from Person
//不能降低从 Person类 继承的方法的可见性
// TODO Auto-generated method stub
}
}
PersonStudentTest测试类:
package com.rucoding.day11_1;
/**
* @author LiuYiXin
*
*/
public class PersonStudentTest {
public static void main(String[] args) {
Student s = new Student("计算机科学与技术");
s.eat(); //重写的方法
s.walk(100); //重写的方法
s.study();//子类自定义拓展的方法
Person p = new Person();
p.eat();
}
}
小结:
方法重写的规定:
格式声明:
权限修饰符 返回值类型 方法名(形参列表) {
//执行的方法体
}
约定俗称: 子类的叫重写的方法, 父类的叫被重写的方法
①.子类重写的方法的方法名和形参列表必须和父类被重写的方法的方法名、形参列表相同; (两同:方法名、形参列表相同)
②.子类重写的方法使用的访问权限修饰符不能小于父类被重写的方法的访问权限修饰符(代码示例已测试)
特殊情况: 子类不能重写父类使用声明为private权限修饰符方法。
③.返回值类型:
1).父类被重写的方法返回值类型是void,则子类重写的方法的返回值类型只能是void;
2).父类被重写的方法返回值类型是基本数据类型(比如是double类型),则子类被重写的方法的返回值类型必须是相同的类型(必须是double类型)
3).父类被重写的方法返回值类型是A类型,则子类被重写的方法的返回值类型可以是A类型或者是A类的子类
4).子类的方法抛出的异常不能大于父类被重写的方法抛出的异常。
注意: 子类与父类中同方法名、同形参列表的方法必须是同时声明为非static的 (即为重写)
如果是同时声明为static的(不是重写),因为static修饰的方法是类加载时与类绑定在一起的,属于静态绑定的。
可以理解重写的意思是重新定义父类的虚函数,但是虚函数是动态绑定的, 而静态方法是静态绑定的,所以静态函数必然不能是虚函数,也就不存在重写了。
3、四种权限修饰符
测试代码:
Order类:
package com.rucoding.day11_1;
/**
* @author LiuYiXin
*
*/
public class Order {
//体会四种权限修饰符的使用范围
//定义变量
private int orderPrivate;
int orderDefault;
protected int orderProtected;
public int orderPublic;
//方法
private void orderPrivateMethod(){
orderPrivate = 1;
orderDefault = 1;
orderProtected = 1;
orderPublic = 1;
}
void orderDefaultMethod(){
orderPrivate = 1;
orderDefault = 1;
orderProtected = 1;
orderPublic = 1;
}
protected void orderProtectedMethod(){
orderPrivate = 1;
orderDefault = 1;
orderProtected = 1;
orderPublic = 1;
}
public void orderPublicMethod(){
orderPrivate = 1;
orderDefault = 1;
orderProtected = 1;
orderPublic = 1;
}
}
同一个包下的类:
package com.rucoding.day11_1;
/**
* @author LiuYiXin
*
*/
public class OrderTest {
public static void main(String[] args) {
Order order = new Order();
//访问属性
order.orderDefault = 2;
order.orderProtected = 2;
order.orderPublic = 2;
//私有化属性只能在本类中访问,其他类无法访问
// order.orderPrivate = 2;//The field Order.orderPrivate is not visible 可见性
//访问方法
order.orderDefaultMethod();
order.orderProtectedMethod();
order.orderPublicMethod();
//私有化的方法只能在本类中访问, 其他类无法访问
// order.orderPrivateMethod();//The method orderPrivateMethod() from the type Order is not visible
}
}
不同包下的子类:
package com.rucoding.day11_2;
import com.rucoding.day11_1.Order;
/**
* @author LiuYiXin
*
*/
public class SubOrder extends Order {
public void method(){
//访问属性
orderProtected = 2;
orderPublic = 2;
//不同包下的子类,私有化的以及缺省的属性无法访问
// orderDefault = 2;//The field Order.orderDefault is not visible
// orderPrivate = 2;
//访问方法
orderProtectedMethod();
orderPublicMethod();
//在不同包的子类中,不能调用Order类中声明为private和缺省的权限的属性、方法
// orderDefaultMethod();
// orderPrivateMethod();
}
}
不同包下的普通类:
package com.rucoding.day11_2;
import com.rucoding.day11_1.Order;
/**
* @author LiuYiXin
*
*/
public class OrderTest {
public static void main(String[] args) {
Order order = new Order();
order.orderPublic = 2;
order.orderPublicMethod();
//不同包下的普通类要使用Order类,不能调用声明为private、缺省、protected权限的属性以及方法
// order.orderPrivate = 2;
// order.orderDefault = 2;
// order.orderProtected = 2;
}
}
4、super关键字
代码演示:
Person类:
package com.rucoding.day12;
/**
* @author LiuYiXin
*
*/
public class Person {
String name;
int age;
int id = 1003;
//初始化构造器
public Person(){
System.out.println("构造器我无处不在的。。。");
}
public Person(String name){
this.name = name;
}
/**
* @param name
* @param age
*/
public Person(String name, int age) {
// super();
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("人总要吃饭的");
}
public void walk(){
System.out.println("没事可以多走走");
}
}
Student类继承于Person类:
package com.rucoding.day12;
/**
* @author LiuYiXin
*
*/
public class Student extends Person {
// 继承Person类的属性和方法
String major;
int id = 1002;
/**
*
*/
public Student() {
// TODO Auto-generated constructor stub
}
public Student(String name, int age, String major) {
super(name, age); //直接通过super关键字调用父类已经定义好的有参构造器
// this.name = name;
// this.age = age;
this.major = major;
}
/**
* @param major
*/
public Student(String major) {
// super();
this.major = major;
}
public void eat() {
System.out.println("学生应该多吃点有营养的东西");
}
public void study() {
System.out.println("学生需要花时间多学习");
this.eat(); //子类重写父类的方法,调用对象执行,子类覆盖父类的方法
//如果,此时想调用父类的方法, 可以使用关键字super
super.eat();
this.walk();//这里此时,子类没有重写父类的方法,用this关键和super关键字调用都可以的
}
public void show() {
System.out.println("name = " + this.name + ",age = " + super.age);
System.out.println("id = " + this.id); //本对象的id
System.out.println("id = " + super.id);//super表示父类的id
}
}
测试类:
package com.rucoding.day12;
/**
* @author LiuYiXin
*
*/
public class SuperTest {
public static void main(String[] args) {
Student s = new Student();
s.show();
Student s1 = new Student("Jack", 21, "IT");
s1.show();
Student s2 = new Student(); //创建子类的对象, 继承的父类Person结构也会被一起加载
s2.study();
}
}
小结
*
* super关键字的使用
* 1.super理解为:父类的
* 2.super可以用来调用:属性、方法、构造器
*
* 3.super的使用
* 3.1 我们可以在子类的方法或构造器中,通过"super.属性"或"super.方法"的方式,显式的调用
* 父类中声明的属性或方法。但是,通常情况下,我们习惯去省略这个"super."
* 3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的
* 使用"super.属性"的方式,表明调用的是父类中声明的属性。
* 3.3 特殊情况:当子类重写了父类中的方法后,我们想在子类的方法中调用父类中被重写的方法时,必须显式的
* 使用"super.方法"的方式,表明调用的是父类中被重写的方法。
*
* 4.super调用构造器
* 4.1 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
* 4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
* 4.3 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现。
* 4.4 在构造器的首行,既没有显式的声明"this(形参列表)"或"super(形参列表)",则默认的调用的是父类中的空参构造器。 super()
* 4.5 在类的多个构造器中,至少有一个类的构造器使用了"super(形参列表)",调用父类中的构造器。
5、子类对象实例化过程
图示描述:
子类对象实例化过程:
1.从结果上看:
子类继承父类以后,就获取了父类中声明的属性和方法:
创建子类对象中, 在堆空间中,就会在加载父类中声明的属性。
2.从过程上看:
当我们通过子类构造器创建子类对象时,我们一定会直接或者间接的调用其父类的构造器
直接调用了java.lang.Object类中的空参的构造器为止。正因为我们加载了父类的结构,所以才能在内存中看到父类的结构,子类对象
考虑进行调用。
明确点: 虽然我们创建子类的对象时,调用了父类的构造器,但自始至终就创建过一个对象,就是new的子类对象。
练习:
/*
* 写一个名为Account的类模拟账户。该类的属性和方法如下图所示。
* 该类包括的属性:账号id,余额balance,年利率annualInterestRate;
* 包含的方法:访问器方法(getter和setter方法),
* 返回月利率的方法getMonthlyInterest(),
* 取款方法withdraw(),存款方法deposit()。
*
*/
Account类:
package com.rucoding.day12;
/**
* @author LiuYiXin
*
*/
public class Account {
/*
* 写一个名为Account的类模拟账户。该类的属性和方法如下所示。
* 该类包括的属性:账号id,余额balance,年利率annualInterestRate;
* 包含的方法:访问器方法(getter和setter方法),
* 返回月利率的方法getMonthlyInterest(),
* 取款方法withdraw(),存款方法deposit()。
*
*/
private int id; //账号
private double balance; //账户余额
private double annualInterestRate; //年利率
/**
*
*/
public Account() {
super();
}
/**
* @param id
* @param balance
* @param annualInterestRate
*/
public Account(int id, double balance, double annualInterestRate) {
super();
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* @return the balance
*/
public double getBalance() {
return balance;
}
/**
* @param balance the balance to set
*/
public void setBalance(double balance) {
this.balance = balance;
}
/**
* @return the annualInterestRate
*/
public double getAnnualInterestRate() {
return annualInterestRate;
}
/**
* @param annualInterestRate the annualInterestRate to set
*/
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
// 返回月利率的方法getMonthlyInterest(),
public double getMonthlyInterest(){
return annualInterestRate / 12;
}
//取款方法
public void withdraw(double amount){
if(balance >= amount){
balance -= amount;
return;
}
System.out.println("余额不足!!!");
}
//存款方法
public void deposit(double amount){
if(amount > 0){
balance += amount;
}
}
}
AccountTest类:
package com.rucoding.day12;
/**
* @author LiuYiXin
*
*/
public class AccountTest {
public static void main(String[] args) {
/*
* 写一个用户程序测试Account类。在用户程序中,
* 创建一个账号为1122、余额为20000、年利率4.5%的Account对象。
* 使用withdraw方法提款30000元,并打印余额。再使用withdraw方法提款2500元,
* 使用deposit方法存款3000元,然后打印余额和月利率。
*/
Account account = new Account(1122, 20000, 0.045);
account.withdraw(30000);
System.out.println("余额为: " + account.getBalance());
account.withdraw(2500);
System.out.println("余额为: " + account.getBalance());
account.deposit(3000);
System.out.println("余额为: " + account.getBalance());
System.out.println("年利率为:" + account.getAnnualInterestRate());
}
}
继承关系:
/*
* 创建Account类的一个子类CheckAccount代表可透支的账户, 该账户中定义一个属性overdraft代表可透支限额。
* 在CheckAccount类中重写withdraw方法,其算法如下: 如果(取款金额<账户余额), 可直接取款 如果(取款金额>账户余额),
* 计算需要透支的额度 判断可透支额overdraft是否足够支付本次透支需要,如果可以 将账户余额修改为0,冲减可透支金额 如果不可以
* 提示用户超过可透支额的限额
*
*/
CheckAccount类:
package com.rucoding.day12;
/**
* @author LiuYiXin
*
*/
public class CheckAccount extends Account {
/*
* 创建Account类的一个子类CheckAccount代表可透支的账户, 该账户中定义一个属性overdraft代表可透支限额。
* 在CheckAccount类中重写withdraw方法,其算法如下: 如果(取款金额<账户余额), 可直接取款 如果(取款金额>账户余额),
* 计算需要透支的额度 判断可透支额overdraft是否足够支付本次透支需要,如果可以 将账户余额修改为0,冲减可透支金额 如果不可以
* 提示用户超过可透支额的限额
*
*/
private double overdraft; // 可透支限额
/**
*
*/
public CheckAccount() {
super();
}
/**
* @param overdraft
*/
public CheckAccount(int id, double balance, double annualInterestRate, double overdraft) {
super(id, balance, annualInterestRate);
this.overdraft = overdraft;
}
/**
* @return the overdraft
*/
public double getOverdraft() {
return overdraft;
}
/**
* @param overdraft
* the overdraft to set
*/
public void setOverdraft(double overdraft) {
this.overdraft = overdraft;
}
/*
* (non-Javadoc)
*
* @see com.rucoding.day12.Account#withdraw(double)
*/
@Override
public void withdraw(double amount) {
if (amount < getBalance()) { // 余额充足
// 可以直接取款
// 这样接收可不行哦,赋值左边必须是一个变量的
// getBalance() -= getBalance() -amount;//The left-hand side of an assignment must be a variabl
setBalance(getBalance() - amount); // 方式一
// 方式二
// super.withdraw(amount); //父类的取款方法
} else if (overdraft >= amount - getBalance()) { // 余额不足
// 可以透支支付
overdraft -= amount - getBalance();
setBalance(0);// 方式一
// super.withdraw(getBalance());//方式二 父类的取款方法
} else {
System.out.println("超过可透支余额了!!");
}
}
}
CheckAccountTest类:
package com.rucoding.day12;
/**
* @author LiuYiXin
*
*/
public class CheckAccountTest {
/*
* 写一个用户程序测试CheckAccount类。在用户程序中,
* 创建一个账号为1122、余额为20000、年利率4.5%,
* 可透支限额为5000元的CheckAccount对象。
* 使用withdraw方法提款5000元,并打印账户余额和可透支额。
* 再使用withdraw方法提款18000元,并打印账户余额和可透支额。
* 再使用withdraw方法提款3000元,并打印账户余额和可透支额。
*
*/
public static void main(String[] args) {
CheckAccount checkAccount = new CheckAccount(1122, 20000, 0.045, 5000);
checkAccount.withdraw(5000);
System.out.println("账户余额: " + checkAccount.getBalance() + " 可透支额度: " + checkAccount.getOverdraft());
checkAccount.withdraw(18000);
System.out.println("账户余额: " + checkAccount.getBalance() + " 可透支额度: " + checkAccount.getOverdraft());
checkAccount.withdraw(3000);
System.out.println("账户余额: " + checkAccount.getBalance() + " 可透支额度: " + checkAccount.getOverdraft());
}
}
6、面向对象特征之三:多态性
面向对象之三: 多态性
1)、理解多态性: 可以理解为一个事物的多态性
2)、何为多态性: 对象的多态性,父类的引用指向子类的对象(Person p = new Man()) 或者子类的对象赋值给父类的引用。
3)、多态的使用: 虚拟方法的调用
有了对象的多态性以后,我们在编译时期,只能调用父类声明的方法,但是在执行期实际执行的是子类重写的方法
简称: 编译时期,看左边; 运行时期,看右边。
若是编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)。
多态情况下,
“看左边”: 看的是父类的引用 (父类中不具备子类特有的方法)
“看右边”: 看的是子类的对象 (实际运行的是子类重写父类的方法)
4)、多态性的使用前提:
①.类的继承关系
②.方法的重写
5)、对象的多态性: 只适用于方法,不适用于属性(属性没有重写的概念,编译和运行期都是看左边)
代码验证:
Person类:
package com.rucoding.day12_1;
/**
* @author LiuYiXin
*
*/
public class Person {
String name;
int age;
public void eat(){
System.out.println("人,吃饭");
}
public void walk(){
System.out.println("人,走路");
}
}
子类Woman类:
package com.rucoding.day12_1;
/**
* @author LiuYiXin
*
*/
public class Woman extends Person{
boolean isBeauty;
public void goShopping(){
System.out.println("女生喜欢购物...");
}
/* (non-Javadoc)
* @see com.rucoding.day12_1.Person#eat()
*/
@Override
public void eat() {
System.out.println("女生少吃,为了减肥");
}
/* (non-Javadoc)
* @see com.rucoding.day12_1.Person#walk()
*/
@Override
public void walk() {
System.out.println("女生,走路淑女。。");
}
}
子类Man类:
package com.rucoding.day12_1;
/**
* @author LiuYiXin
*
*/
public class Person {
String name;
int age;
public void eat(){
System.out.println("人,吃饭");
}
public void walk(){
System.out.println("人,走路");
}
}
测试类:
package com.rucoding.day12_1;
/**
* @author LiuYiXin
*
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
Man man = new Man();
man.eat();
man.age = 24;
man.earnMoney();
//========================
System.out.println("=========================");
//对象的多态性,父类的引用指向子类对象
Person p2 = new Man();
Person p3 = new Woman();
//多态的使用,当调用父类同名同形参列表方法时, 实际是调用子类对象重写父类的方法---虚拟方法调用
p2.eat();
p2.walk();
//编译通过不了,这个方法在父类没有定义,多态的前提之一是基于被重写的方法
// p2.earnMoney();//The method earnMoney() is undefined for the type Person
p3.eat();
p3.walk();
// p3.goShopping();//
}
}
多态性应用举例:
package com.rucoding.day12_1;
/**
* @author LiuYiXin
*
*/
public class AnimalTest {
//程序主入口
public static void main(String[] args) {
//此时调用Dog和Cat的方法,
AnimalTest a = new AnimalTest();
a.func(new Dog());
a.func(new Cat());
System.out.println("********多态性的应用********");
Animal aDog = new Dog();
a.func(aDog);
Animal aCat = new Cat();
a.func(aCat);
}
//多态性的应用举例
public void func(Animal animal){ //Animal aDog = new Dog();
animal.eat();
animal.shout();
}
//如果没有多态性,就会写如下很多的方法,去调用
public void func(Dog dog){
dog.eat();
dog.shout();
}
public void func(Cat cat){
cat.eat();
cat.shout();
}
}
class Animal{
//方法
public void eat(){
System.out.println("动物,吃饭");
}
public void shout(){
System.out.println("动物,吼叫");
}
}
class Dog extends Animal{
/* (non-Javadoc)
* @see com.rucoding.day12_1.Animal#eat()
*/
@Override
public void eat() {
System.out.println("狗吃骨头");
}
/* (non-Javadoc)
* @see com.rucoding.day12_1.Animal#shout()
*/
@Override
public void shout() {
System.out.println("狗汪汪叫");
}
}
class Cat extends Animal{
/* (non-Javadoc)
* @see com.rucoding.day12_1.Animal#eat()
*/
@Override
public void eat() {
System.out.println("猫吃鱼的");
}
/* (non-Javadoc)
* @see com.rucoding.day12_1.Animal#shout()
*/
@Override
public void shout() {
System.out.println("猫是我们一起喵喵喵");
}
}
6.1、虚拟方法的补充
面试题:多态性是编译时行为还是运行时行为
代码演示:
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
import java.util.Random;
/*
* 2.从编译和运行的角度看:
* 重载,是指允许存在多个同名方法,而这些方法的参数不同。
* 编译器根据方法不同的参数表,对同名方法的名称做修饰。
* 对于编译器而言,这些同名方法就成了不同的方法。
* 它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,
* 即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,
* 编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
* 而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,
* 这称为“晚绑定”或“动态绑定”。
*
* 引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
*/
//面试题:多态是编译时行为还是运行时行为?
//证明如下:
public class InterviewTest {
//程序主入口
public static void main(String[] args) {
int key = new Random().nextInt(3); //产生0~2的随机数
System.out.println(key);
Animal animal = getInsance(key); //运行时行为
animal.eat();
}
public static Animal getInsance(int key){
switch(key){
case 0:
return new Dog();
case 1:
return new Cat();
default:
return new Sheep();
}
}
}
//编写类,设计类的属性和方法
class Animal{
//
protected void eat(){
System.out.println("animal eat foods");
}
}
class Dog extends Animal{
/* (non-Javadoc)
* @see com.rucoding.day12_2.Animal#eat()
*/
@Override
protected void eat() {
// TODO Auto-generated method stub
// super.eat();
System.out.println("dog eat bone");
}
}
class Cat extends Animal{
/* (non-Javadoc)
* @see com.rucoding.day12_2.Animal#eat()
*/
@Override
protected void eat() {
// TODO Auto-generated method stub
//super.eat();
System.out.println("cat eat fishes");
}
}
class Sheep extends Animal{
/* (non-Javadoc)
* @see com.rucoding.day12_2.Animal#eat()
*/
@Override
protected void eat() {
// TODO Auto-generated method stub
// super.eat();
System.out.println("sheep eat grass");
}
}
小结
/*
* 1).从编译和运行的角度看:
* 重载,是指允许存在多个同名方法,而这些方法的参数不同。
* 编译器根据方法不同的参数表,对同名方法的名称做修饰。
* 对于编译器而言,这些同名方法就成了不同的方法。
* 它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,
* 即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,
* 编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
* 而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,(示例代码的getInstance(key)方法)
* 这称为“晚绑定”或“动态绑定”。
*
* 引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
*/
6.2、向下转型的使用
Person类:
package com.rucoding.day12_1;
/**
* @author LiuYiXin
*
*/
public class Person {
String name;
int age;
public void eat(){
System.out.println("人,吃饭");
}
public void walk(){
System.out.println("人,走路");
}
}
Man类:
package com.rucoding.day12_1;
/**
* @author LiuYiXin
*
*/
public class Man extends Person{
boolean isSmoking;
public void earnMoney(){
System.out.println("男生负责养家糊口");
}
/* (non-Javadoc)
* @see com.rucoding.day12_1.Person#eat()
*/
@Override
public void eat() {
System.out.println("男生多吃水果青菜,少吃肉,少喝老火汤");
}
/* (non-Javadoc)
* @see com.rucoding.day12_1.Person#walk()
*/
@Override
public void walk() {
System.out.println("男生走路大摇大摆");
}
}
Woman类:
package com.rucoding.day12_1;
/**
* @author LiuYiXin
*
*/
public class Woman extends Person{
boolean isBeauty;
public void goShopping(){
System.out.println("女生喜欢购物...");
}
/* (non-Javadoc)
* @see com.rucoding.day12_1.Person#eat()
*/
@Override
public void eat() {
System.out.println("女生少吃,为了减肥");
}
/* (non-Javadoc)
* @see com.rucoding.day12_1.Person#walk()
*/
@Override
public void walk() {
System.out.println("女生,走路淑女。。");
}
}
测试类:
package com.rucoding.day12_1;
import java.util.Date;
/**
* @author LiuYiXin
*
*/
public class PersonTest2 {
public static void main(String[] args) {
Person p2 = new Man();
// 不能调用子类所特有的方法、属性,编译时,p2是Person类型,
//The method earnMoney() is undefined for the type Person
// p2.earnMoney(); //编译时期,p2是Person类型的
p2.name = "Tom";
// 有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法,但是由于量声明
// 变为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类的属性和方法不能调用。
// p2.isSmoking = true;
//问题就来了, 如何调用子类的属性和方法结构呢 ??
//既然p2是父类的Person类型,转回去Man类型呢?
//使用了强制类型转换符,也可以称为:向下转型
Man m1 = (Man)p2;
m1.isSmoking = true;
m1.earnMoney();
//使用强制类型转换,也可能会出现异常ClassCastException
// Woman w1 = (Woman)p2; //编译通过了,运行时抛异常java.lang.ClassCastException
// w1.goShopping();//编译通过了
//如何避免ClassCastException异常的出现呢?
//可以判断一下,当我们进行向下转型的时候,判断类型
/**
* ①.判断的关键字instancof
* a instanceof A : 判断对象a是否是类A的实例,如果是,返回true,否则返回false;
* ②.使用情境:为了避免在向下转型时出现ClassCastException异常,我们在进行向下转型之前,先进行
* instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
* ③.如果a instanceof A返回true,则a instanceof B也返回true。 其中类B是类A的父类。
*/
if(p2 instanceof Woman){
Woman w1 = (Woman)p2;
w1.goShopping();
System.out.println("类型Woman");
}
if(p2 instanceof Man){ //a instanceof A ==> a对象是A类的实例
Man m2 = (Man)p2;
m2.isSmoking = true;
m2.earnMoney();
System.out.println("类型Man");
}
if(p2 instanceof Person){ //a instanceof B ==> B类是A类的父类
System.out.println("类型Person");
}
if(p2 instanceof Object){//a instanceof C ==> C类是B类的父类
System.out.println("类型Obejct");
}
//向下转型的常见问题
//编译时能通过,运行时不通过
//举例一
// Person p3 = new Woman();
// Man m3 = (Man)p3;
//举例二
Person p4 = new Person();
Man m4 = (Man)p4;
Object o = new Date();
String str1 = (String)o;
//编译不通过
// Man m5 = new Woman();
// String str = new Date();
}
}
小结
1)、多态性的使用前提:
① 类的继承关系
② 方法的重写
2)、对象的多态性:只适用于方法,不适用于属性(编译和运行都看左边,属性没有重写的概念)
3)、有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法,但是由于变量声明为父类类型,导致
编译时,只能调用父类中声明的属性和方法。子类的属性和方法不能调用。
4)、调用子类的属性和方法:向下转型 (可能抛异常ClassCastException)
5)、判断类型:instanceof关键字的使用
①.a instanceof A : 判断对象a是否是类A的实例,如果是,返回true,否则返回false;
②.使用情境:为了避免在向下转型时出现ClassCastException异常,我们在进行向下转型之前,先进行
instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
③.如果a instanceof A返回true,则a instanceof B也返回true。 其中类B是类A的父类。
6)、向下转型的常见问题:(详见上述测试代码)
①.编译时能通过,运行时不通过
②.编译不通过
练习1
代码演示:
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
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; //子类的对象赋值给Base b父类的引用 ,多态性的体现
System.out.println(b == s); //==比较的是地址值,赋值,引用指向同一个地址值,true
System.out.println(b.count);//属性没有重写的概念,编译和运行时期还是看左边Base,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
/*
* 建立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.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
/*
* 建立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”
*
*/
public class InstanceTest {
public static void main(String[] args) {
InstanceTest instanceTest = new InstanceTest();
instanceTest.method(new Graduate());
}
public void method(Person e) {
System.out.println(e.getInfo());
if (e instanceof Graduate) {
System.out.print("a graduated student" + " ");
}
if (e instanceof Student) {
System.out.print("a student" + " ");
}
if (e instanceof Person) {
System.out.print("a person");
}
}
}
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;
}
}
练习3
/*
* 定义三个类,父类GeometricObject代表几何形状,子类Circle代表圆形,MyRectangle代表矩形。
*/
父类GeometricObject:
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
public class GeometricObject {
protected String color;
protected double weight;
/**
* @return the color
*/
public String getColor() {
return color;
}
/**
* @param color the color to set
*/
public void setColor(String color) {
this.color = color;
}
/**
* @return the weight
*/
public double getWeight() {
return weight;
}
/**
* @param weight the weight to set
*/
public void setWeight(double weight) {
this.weight = weight;
}
/**
*
*/
public GeometricObject() {
super();
}
/**
* @param color
* @param weight
*/
public GeometricObject(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
//面积方法
public double findArea(){
return 0.0;
}
}
子类Circle:
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
public class Circle extends GeometricObject{
private double radius;
public Circle(String color, double weight,double radius){
super(color,weight);
this.radius = radius;
}
/**
* @return the radius
*/
public double getRadius() {
return radius;
}
/**
* @param radius the radius to set
*/
public void setRadius(double radius) {
this.radius = radius;
}
//求圆的面积
/* (non-Javadoc)
* @see com.rucoding.day12_2.GeometricObject#findArea()
*/
@Override
public double findArea() {
return Math.PI * radius * radius;
}
}
子类MyRectangle:
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
public class MyRectangle extends GeometricObject{
private double width;
private double height;
/**
*
*/
public MyRectangle() {
super();
}
//
public MyRectangle(double width,double height,String color, double weight){
super(color,weight);
this.width = width;
this.height = height;
}
/**
* @return the width
*/
public double getWidth() {
return width;
}
/**
* @param width the width to set
*/
public void setWidth(double width) {
this.width = width;
}
/**
* @return the height
*/
public double getHeight() {
return height;
}
/**
* @param height the height to set
*/
public void setHeight(double height) {
this.height = height;
}
/* (non-Javadoc)
* @see com.rucoding.day12_2.GeometricObject#findArea()
*/
@Override
public double findArea() {
// TODO Auto-generated method stub
return width * height;
}
}
测试类:
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
/*
* 定义一个测试类GeometricTest,编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术),
* 编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
*
*/
public class GeometricTest {
public static void main(String[] args) {
GeometricTest geometricTest = new GeometricTest();
Circle c1 = new Circle("白色", 2.14, 1.5);
geometricTest.displayGeometricObject(c1);
Circle c2 = new Circle("红色", 2.14, 1.5);
geometricTest.displayGeometricObject(c2);
//比较面积是否相等
boolean isEqual = geometricTest.equalsArea(c1, c2);
System.out.println("面积是否相等:" + (isEqual == true ? '是' : '否'));
MyRectangle myRectangle = new MyRectangle(4.2, 8.4, "蓝色", 12);
geometricTest.displayGeometricObject(myRectangle);
}
//displayGeometricObject方法显示对象的面积
public void displayGeometricObject(GeometricObject o){
double area = o.findArea();
if(o instanceof Circle){
System.out.println("圆的面积为:" + area);
}else{
System.out.println("矩形的面积为:" + area);
}
}
//equalsArea两个对象的面积是否相等
public boolean equalsArea(GeometricObject o1,GeometricObject o2){
return (o1.findArea() == o2.findArea()) ? true : false;
}
}
面试题拓展:
/* 考查多态的笔试题目:
* 面试题:多态是编译时行为还是运行时行为?如何证明? 详见【6.1、虚拟方法的补充】
*
* 拓展问题
*/
代码演示:
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
public class InterviewTest1 {
public static void main(String[] args) {
Base1 b = new Sub1();
b.add(1,2,3);
Sub1 sub1 = (Sub1)b;
sub1.add(1, 2, 3);//精准调用已有的形参列表
}
}
class Base1{
public void add(int a ,int...arr){//可变形参
System.out.println("base1");
}
}
class Sub1 extends Base1{
@Override
public void add(int a,int[] arr){//可变形参,本质就是数组的int[] arr
System.out.println("sub1"); //是重写的方法 .同名同形参列表
}
public void add(int a,int b,int c){
System.out.println("sub2");
}
}
7、Object类的使用
1)、Object类是所有类的根父类
2)、如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
3)、Object类中的功能(属性、方法)具有通用性。
Object类的属性: 无
Object类的方法: equals() / toString() getClass() / hashCode() clone() /finalize() wait() 、notify()、
notifyAll()
4)、Object类只声明了一个空参的构造器。
7.1、Object类主要结构
7.2、== 和 equals 方法
一、回顾 == 运算符的使用
1)、可以使用在基本数据类型和引用数据类型中
2)、如果比较的是基本数据类型变量:比较的是两个变量保存的数据是否相等(存储的数据值,不一定类型要一致的)
如果比较的是引用数据类型变量:比较的是两个对象的地址值是否相同,即是两个引用的指向是否为同一个对象的实体
补充: == 符号使用时,必须保证符号左右两边的变量类型一致。
二、equals()方法的使用
1)、equals()是方法,并非运算符
2)、只能适用于引用数据类型
3)、Object中定义的equals方法
public boolean equals(Object obj) {
return (this == obj);
}
说明:从代码看,Object的equals()方法 跟 == 的作用是相同的,比较的是两个对象的地址值是否相同,即是两个引用的指向是否为 同一个对象的实体。
4)、像 String、Date、File、包装类等都重写了Object类中的equals()方法.
public String toString() {
return this;
}
5)、通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们
就需要对Object类中的equals()进行重写。
重写的原则:比较两个对象的实体内容是否相同。
代码演示:
package com.rucoding.day12_2;
import java.util.Date;
/**
* @author LiuYiXin
*
*/
public class EqualsTest {
public static void main(String[] args) {
//基本数据类型
int i = 10;
int j = 10;
double d = 10.0;
System.out.println(i == j);//true
System.out.println(i == d);//true,基本数据类型比较的是真实的存储数据,类型可以不一致
//boolean类型肯定是不纳入比较的
// boolean b = true;
// System.out.println(i == b);
char c = 10;
System.out.println(c);
System.out.println(i == c); //true
char c1 = 'A';
char c2 = 65;
System.out.println(c1 == c2);//true
System.out.println("========引用数据类型========");
//引用数据类型
Customer cust1 = new Customer("Tom", 21);
Customer cust2 = new Customer("Jack",22);
System.out.println(cust1 == cust2);//false 地址值不同
String str1 = new String("BAT");
String str2 = new String("BAT");
System.out.println(str1 == str2); //false
System.out.println(cust1.equals(cust2));//false //此时调用的是父类Object的equals方法
System.out.println(str1.equals(str2));//true
Date date1 = new Date(23432525324L);
Date date2 = new Date(23432525324L);
System.out.println(date1.equals(date2));//true
}
}
Customer类:
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
public class Customer {
private String name;
private int age;
/**
*
*/
public Customer() {
super();
}
/**
* @param name
* @param age
*/
public Customer(String name, int age) {
super();
this.name = name;
this.age = age;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
//假如比较两个对象的实体内容是否相同,Customer类定义的属性name和age
public boolean equals(Object obj){
if(this == obj){ //地址值相同,直接是引用指向同一个对象实体
return true;
}
if(obj instanceof Customer){
Customer cust = (Customer)obj;
if(this.age == cust.age && this.name.equals(cust.name)){
return true;
}
}else{
return false;
}
return false;
}
}
练习1:
/*
* .编写Order类,有int型的orderId,String型的orderName,
* 相应的getter()和setter()方法,两个参数的构造器,重写父类的equals()方法:public booleanequals(Object obj),
* 并判断测试类中创建的两个对象是否相等。
*
*
*/
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
public class OrderTest {
public static void main(String[] args) {
OrderTest orderTest = new OrderTest();
Order order1 = new Order(1001, "长江七号");
Order order2 = new Order(1001, "长江七号");
System.out.println(order1.equals(order2));
Order order3 = new Order(1002, "长江七号");
System.out.println(order2.equals(order3));
}
}
class Order {
/**
* @param orderId
* @param orderName
*/
private int orderId;
private String orderName;
/**
* @param orderId
* @param orderName
*/
public Order() {
}
/**
* @param orderId
* @param orderName
*/
public Order(int orderId, String orderName) {
super();
this.orderId = orderId;
this.orderName = orderName;
}
/**
* @return the orderId
*/
public int getOrderId() {
return orderId;
}
/**
* @param orderId
* the orderId to set
*/
public void setOrderId(int orderId) {
this.orderId = orderId;
}
/**
* @return the orderName
*/
public String getOrderName() {
return orderName;
}
/**
* @param orderName
* the orderName to set
*/
public void setOrderName(String orderName) {
this.orderName = orderName;
}
// 重写equals方法
@Override
public boolean equals(Object obj) {
if (this == obj) { // 比较的对象的地址值相同,即是引用指向同一个对象实体
return true;
}
if (obj instanceof Order) {
Order order = (Order) obj;
if (this.orderId == order.orderId && this.orderName.equals(order.orderName)) {
return true;
}
} else {
return false;
}
return false;
}
}
练习2:
/*
* 请根据以下代码自行定义能满足需要的MyDate类,在MyDate类中覆盖equals方法,
* 使其判断当两个MyDate类型对象的年月日都相同时,结果为true,否则为false。
* public boolean equals(Object o)
*/
代码演示:
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
/*
* 请根据以下代码自行定义能满足需要的MyDate类,在MyDate类中覆盖equals方法,
* 使其判断当两个MyDate类型对象的年月日都相同时,结果为true,否则为false。 public boolean equals(Object o)
*/
public class MydateTest {
public static void main(String[] args) {
MyDate myDate1 = new MyDate(14, 3, 2019);
MyDate myDate2 = new MyDate(14, 3, 2019);
if(myDate1 == myDate2){
System.out.println("myDate1 == myDate2");
}else{
System.out.println("myDate1 != myDate2");
}
if(myDate1.equals(myDate2)){
System.out.println("myDate1 is equal myDate2");
}else{
System.out.println("myDate1 is not equal myDate2");
}
}
}
class MyDate {
private int day;
private int month;
private int year;
/**
*
*/
public MyDate() {
super();
}
/**
* @param day
* @param month
* @param year
*/
public MyDate(int day, int month, int year) {
super();
this.day = day;
this.month = month;
this.year = year;
}
/**
* @return the day
*/
public int getDay() {
return day;
}
/**
* @param day
* the day to set
*/
public void setDay(int day) {
this.day = day;
}
/**
* @return the month
*/
public int getMonth() {
return month;
}
/**
* @param month
* the month to set
*/
public void setMonth(int month) {
this.month = month;
}
/**
* @return the year
*/
public int getYear() {
return year;
}
/**
* @param year
* the year to set
*/
public void setYear(int year) {
this.year = year;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof MyDate) {
MyDate myDate = (MyDate) obj;
if (this.day == myDate.day && this.month == myDate.month && this.year == myDate.year) {
return true;
}
}
return false;
}
}
7.3、toString()的使用
/*
* Object类中toString()的使用
*
* 1.当我们输出一个引用对象时,实际上就是调用当前对象的toString()
* 2.Object类中toString的定义方法
* public String toString() {
* return getClass().getName() + "@" + Integer.toHexString(hashCode());
* }
*
* 3.像String、Date、File、包装类等都重写了Object类中的toString()方法。
* 使得在调用toString()时,返回"实体内容"信息.
*
* 4.自定义类如果重写toString()方法,当调用此方法时,返回对象的"实体内容".
*/
代码测试:
package com.rucoding.day12_2;
/**
* @author LiuYiXin
*
*/
public class ToStringTest {
public static void main(String[] args) {
Customer cust1 = new Customer("Tom" ,21);
/*
* 此时打印的是父类的toString方法
* public String toString() {
* return getClass().getName() + "@" + Integer.toHexString(hashCode());
* }
*/
System.out.println(cust1.toString());//com.rucoding.day12_2.Customer@15db9742
//子类Customer重写父类Object的toString()方法
System.out.println(cust1); //Customer [name=Tom, age=21]
}
}
8、包装类(Wrapper)的使用
8.1、单元测试Junit的使用
/*
* java中的JUnit单元测试
*
* 步骤:
* 1.选中当前项目工程 --》 右键:build path --》 add libraries --》 JUnit 4 --》 下一步
* 2.创建一个Java类进行单元测试。
* 此时的Java类要求:①此类是公共的 ②此类提供一个公共的无参构造器
* 3.此类中声明单元测试方法。
* 此时的单元测试方法:方法的权限是public,没有返回值,没有形参。
*
* 4.此单元测试方法上需要声明注解:@Test并在单元测试类中调用:import org.junit.Test;
* 5.声明好单元测试方法以后,就可以在方法体内测试代码。
* 6.写好代码后,左键双击单元测试方法名:右键 --》 run as --》 JUnit Test
*
* 说明:如果执行结果无错误,则显示是一个绿色进度条,反之,错误即为红色进度条。
*/
package com.rucoding.day13;
import org.junit.Test;
/**
* @author LiuYiXin
*
*/
public class JunitTest {
int num = 10;
//单元测试 加注解@Test
@Test
public void testEquals(){
String s1 = "MM";
String s2 = "MM";
System.out.println(s1.equals(s2));
System.out.println(num);
show();
}
public void show(){
num = 20;
System.out.println("show()..." + num);
}
@Test
public void testToString(){ //运行哪个测试方法,双击该方法右键Run As ==> JUnit Test
String s2 = "MM";
System.out.println(s2.toString());
}
}
示意图:
8.2、包装类的使用
/*
* 包装类的使用
* 1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
* 基本数据类型 包装类
* byte Byte
* short Short
* int Integer
* long Long
* float Float
* double Double
* boolean Boolean
* char Character
* 注意:其中Byte、Short、Integer、Long、Float、Double的父类是:Number
* /
8.3、包装类与基本数据类型的转换
代码演示:
package com.rucoding.day13;
import org.junit.Test;
/**
* @author LiuYiXin
*
*/
public class WrapperTest {
// 1.基本数据类型 ==》包装类
@Test
public void test1() {
int num = 10;
// System.out.println(num.toString()); //基本数据类型不是对象
// 包装类
Integer integer = new Integer(num);
System.out.println(integer.toString());
Integer integer2 = new Integer("123");
System.out.println(integer2.toString());
System.out.println(integer2 + 2);
// Integer integer3 = new Integer("123abc");//抛出异常
// System.out.println(integer3.toString());
Float float1 = new Float(12.3);
System.out.println(float1);
Float float2 = new Float("12.3");
System.out.println(float2);
Boolean boolean1 = new Boolean(false);
System.out.println(boolean1);
Boolean boolean2 = new Boolean("true123");// 不会报错?
System.out.println(boolean2); // false
Order order = new Order();
System.out.println(order.isMale);// boolean 基本数据类型,默认false
System.out.println(order.isFemale);// 引用数据类型,默认null
}
//2.包装类 ==》基本数据类型 xxxValue()
@Test
public void test2(){
Integer integer = new Integer(12);
int intValue = integer.intValue();
System.out.println(intValue + 3);
Float float1 = new Float(12.3);
float floatValue = float1.floatValue();
System.out.println(floatValue + 2);
}
/**
* 3.JDK1.5后,新特性,自动装箱,自动拆箱
*/
@Test
public void test3(){
int num = 10;
//自动装箱,基本数据类型==》包装类
Integer integer = num;
boolean b1 = true;
Boolean b2 = b1;
//自动拆箱:包装类==》基本数据类型
int num3 = integer;
}
/**
* 4.基本数据类型、包装类 ==》String类型 。调用String重载的valueOf(Xxx xxx)
*
*/
@Test
public void test4(){
int num = 10;
//转为String类型
String str = num + "";// + 连接操作
String str2 = String.valueOf(num);
System.out.println(str2.toString());
float f1 = 12.3f;
String str3 = String.valueOf(f1);
Double d2 = new Double(12.3);
String str4 = String.valueOf(d2);
System.out.println(str4);
}
/**
* 5.String类型转变为基本数据类型、包装类, 调用包装类的parseXxx()
*
*/
@Test
public void test5(){
String str1 = "123";
int parseInt = Integer.parseInt(str1);
System.out.println(parseInt + 2);
String str2 = "true";
boolean parseBoolean = Boolean.parseBoolean(str2);
System.out.println(parseBoolean); //true
}
}
class Order {
boolean isMale;
Boolean isFemale;
}
小结
1) 基本数据类型 <===> 包装类 , 通过自动装箱、自动拆箱
2) 基本数据类、包装类 ===> String类型 , 通过String重载的valueOf(Xxx xxx)
3) String类型 ===> 基本数据类型、包装类, 通过包装类的parseXxx()方法
练习1:
面试题
package com.rucoding.day13;
/**
* @author LiuYiXin
*
*/
import org.junit.Test;
/*
* 如下两个题目输出结果相同吗?各是什么:
* Object o1= true? new Integer(1) : new Double(2.0);
* System.out.println(o1);//
*
* Object o2;
* if(true)
* o2 = new Integer(1);
* else
* o2 = new Double(2.0);
* System.out.println(o2);//
*
*/
public class InterViewTest {
@Test
public void test() {
Object o1 = true ? new Integer(1) : new Double(2.0);
// 三元运算符,返回值接收必须是满足两个结果的
System.out.println(o1);// 1.0,假如是1 对应的是Integer类型,Double类型不适用
}
@Test
public void test2() {
Object o2;
if (true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2);// 1
}
@Test
public void method1() {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); // false ==比较的是地址值
//看源码吧
// Integer内部定义了一个IntegerCache结构,IntegerCache中定义Integer[]
// 保存了从-128-127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在其中时,
// 可以直接使用数组中的元素,不用再去new了。目的,提高效率。
Integer m = 1;
Integer n = 1;
System.out.println(m == n);// true
Integer x = 128;// 相当于new了一个Integer对象 //看源码
Integer y = 128;// 相当于new了一个Integer对象 //看源码
System.out.println(x == y);// false
}
}
练习2:
代码演示:
package com.rucoding.day13;
import java.util.Scanner;
import java.util.Vector;
/**
* @author LiuYiXin
*
*/
/*
* 利用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等
*
*/
public class VectorTest {
public static void main(String[] args) {
Vector v = new Vector();
int maxScore = 0; //初始化最大值
//声明接收键盘输入的
Scanner scanner = new Scanner(System.in);
for(;;){
System.out.println("请输入学生成绩(以负数代表输入结束)");
int score = scanner.nextInt();
if(score < 0){
//输入的负数,结束循环
break;
}
if(score > 100){
System.out.println("输入的成绩无效,请重新输入");
continue;
}
// 添加操作::v.addElement(Object obj)
// jdk5.0之前:
// Integer inScore = new Integer(score);
// v.addElement(inScore);//多态
// jdk5.0之后:
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);
}
}
}