天天看点

三篇文章彻底搞懂Java面向对象之二

写在前面,Java基础系列文章都是作者基于b站尚硅谷的Java基础视频所做的笔记,没有时间的同学可以认真看看,如果有时间的同学,还是建议看看视频,毕竟笔记说到底还是自己的东西,每个人的习惯也是不一样的,所以为了自己,学就对了,加油!

也请各位同学,如果感觉对自己有帮助,可以帮忙给个一键三连,谢谢!

文章目录

  • ​​1. 面向对象的特征之二:继承性​​
  • ​​2.方法的重写 (override/overwrite)​​
  • ​​2.1 方法重写的练习​​
  • ​​3.四种访问权限修饰符​​
  • ​​4.关键字:super​​
  • ​​5.子类对象实例化的过程​​
  • ​​4.1 super练习​​
  • ​​6.面向对象的特征之三: 多态性​​
  • ​​6.1 虚拟方法的补充​​
  • ​​6.2 向下转型的使用​​
  • ​​6.3 多态性的练习​​
  • ​​6.4 Object类的使用​​
  • ​​6.4.1 Object类中的主要结构​​
  • ​​6.4.2 ==操作符与equals方法​​
  • ​​6.4.3 重写equals( )方法的原则​​
  • ​​6.4.4 toString( )的使用​​
  • ​​6.5 包装类的使用​​
  • ​​6.5.1 单元测试方法的使用​​
  • ​​6.5.2 包装类的使用​​
  • ​​6.5.3 包装类与基本数据类型相互转换​​
  • ​​6.5.4 包装类练习​​

1. 面向对象的特征之二:继承性

Person类

/*
 * 为描述和处理个人信息,定义类 Person
 */
public class Person{
  
  String name;
  private int age;
  
  public Person(){
    
  }
  
  public Person(String name,int age){
    this.name = name;
    this.age = age;
  }
  
  public void eat(){
    System.out.println("吃饭");
    sleep();
  }
  
  private void sleep(){
    System.out.println("睡觉");
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }
  
}      

Student类

/*
 * 为描述和处理学生信息,定义类 Student
 */
public class Student extends Person{
  
//  String name;
//  int age;
  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 eat(){
//    System.out.println("吃饭");
//  }
//  
//  public void sleep(){
//    System.out.println("睡觉");
//  }
  
  public void study(){
    System.out.println("学习");
  }
  
  public void show(){
    System.out.println("name:" + name + ",age:" + getAge());
  }
  
}      

测试类

/*
 * 面向对象的特征之二:继承性 
 * 
 * 为什么要有继承?
 *     多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,
 *     那么多个类无需再定义这些属性和行为,只要继承那个类即可。
 * 
 * 一、继承性的好处:
 * ① 减少了代码的冗余,提高了代码的复用性
 * ② 便于功能的扩展
 * ③ 为之后多态性的使用,提供了前提
 * 
 * 二、继承性的格式: 
 *    class A extends B{ }
 *    A:子类、派生类、subclass
 *    B:父类、超类、基类、superclass
 *    
 *    2.1体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
 *     特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
 *    2.2 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。子类和父类的关系,不同于子集和集合的关系。
 *    extends:延展、扩展
 */
public class ExtendsTest {
  public static void main(String[] args) {
    
    Person p1 = new Person();
//    p1.age = 1;
    p1.eat();
    System.out.println("*****************");
    
    Student s1 = new Student();
    s1.eat();
//    s1.sleep();
    s1.name = "Tom";
    
    s1.setAge(10);
    System.out.println(s1.getAge());
  }
}      
三篇文章彻底搞懂Java面向对象之二
三篇文章彻底搞懂Java面向对象之二
Java中关于继承性的规定
/* 
 * 三、Java中关于继承性的规定:
 *     1.一个类可以被多个子类继承。
 *    2.Java中类的单继承性:一个类只能有一个父类
 *    3.子父类是相对的概念。
 *    4.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
 *    5.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
 *    
 * 四、 1. 如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
 *    2. 所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
 *    3. 意味着,所有的java类具有java.lang.Object类声明的功能。
 */
public class ExtendsTest {

    public static void main(String[] args) {    
    Student s1 = new Student();
        s1.brease();
        
        Creature c = new Creature();
        System.out.println(c.toString());
        
    }
}      

将上述 Person 类改为如下

public class Person extends Creature {
    ...
}      

Creature 类

public class Creature {
  
  public void breath(){
    System.out.println("呼吸");
  }
  
}      
三篇文章彻底搞懂Java面向对象之二
练习1
三篇文章彻底搞懂Java面向对象之二

ManKind类

public class ManKind {

  private int sex;  //性别
  private int salary;  //薪资
  
  public ManKind() {
    
  }

  public ManKind(int sex, int salary) {
    this.sex = sex;
    this.salary = salary;
  }

  public void manOrWoman(){
    if(sex==1){
      System.out.println("man");
    }else if(sex==0){
      System.out.println("woman");
    }
  }
  
  public void employeed(){
    if(salary==0){
      System.out.println("no job");
    }else if(salary!=0){
      System.out.println("job");
    }
  }

  public int getSex() {
    return sex;
  }

  public void setSex(int sex) {
    this.sex = sex;
  }

  public int getSalary() {
    return salary;
  }

  public void setSalary(int salary) {
    this.salary = salary;
  }
  
}      

Kids类

public class Kids extends ManKind{

  private int yearsOld;  //年限
  
  public Kids() {

  }

  public Kids(int yearsOld) {
    this.yearsOld = yearsOld;
  }

  public int getYearsOld() {
    return yearsOld;
  }

  public void setYearsOld(int yearsOld) {
    this.yearsOld = yearsOld;
  }

  public void printAge(){
    System.out.println("I am " + yearsOld);
  }
}      

KidsTest类

/*
 * 定义类KidsTest,在类的main方法中实例化Kids的对象someKid,
 * 用该对象访问其父类的成员变量及方法。
 */
public class KidsTest {
  public static void main(String[] args) {
    
    Kids someKid = new Kids(12);
    
    someKid.printAge();
    
    someKid.setYearsOld(15);
    someKid.setSalary(0);
    someKid.setSex(1);
    
    someKid.employeed();
    someKid.manOrWoman();
  }
}      
练习2
三篇文章彻底搞懂Java面向对象之二

Circle类

public class Circle {

  public double radius;  //半径
  
  public Circle(){
    radius = 1.0;
  }

  public double getRadius() {
    return radius;
  }

  public void setRadius(double radius) {
    this.radius = radius;
  }
  
  public double findArea(){  //计算圆的面积
    return Math.PI * radius * radius;
  }
}      

Cylinder类

public class Cylinder extends Circle{

  private double length;
  
  public Cylinder(){
    length = 1.0;
  }

  public double getLength() {
    return length;
  }

  public void setLength(double length) {
    this.length = length;
  }
  
  public double findVolume(){  //计算圆柱体积
    return findArea() * length;
  }
}      

测试类

public class CylinderTest {
  public static void main(String[] args) {
    
    Cylinder cy = new Cylinder();
    
    cy.setRadius(2.1);
    cy.setLength(3.4);
    double volues = cy.findVolume();
    System.out.println("圆柱的体积:" + volues);
    
    double area = cy.findArea();
    System.out.println("圆的面积: " + area);
  }
}      

2.方法的重写 (override/overwrite)

/*
 * 方法的重写(override / overwrite)
 * 
 * 1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
 * 
 * 2.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
 * 
 * 3. 重写的规定:
 *       方法的声明: 权限修饰符  返回值类型  方法名(形参列表) throws 异常的类型{
 *             //方法体
 *           }
 *       约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
 *     ① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
 *    ② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
 *       > 特殊情况:子类不能重写父类中声明为private权限的方法
 *    ③ 返回值类型:
 *       > 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
 *       > 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
 *       > 父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
 *   ④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理的时候讲)
 * **********************************************************************
 *   子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
 *    因为static方法是属于类的,子类无法覆盖父类的方法
 *
 * 面试题:区分方法的 重载 与 重写(有的书也叫做“覆盖”)
 * 答:方法的重写Overriding和重载Overloading是Java多态性的不同表现。
 *     重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。
 *     如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。
 *     子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被"屏蔽"了。
 *     如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载     (Overloading)。
 * 
 */      

Person类

public class Person {
  
  String name;
  int age;
  
  public Person(){
    
  }
  public Person(String name,int age){
    this.name = name;
    this.age = age;
  }
  
   void eat(){
    System.out.println("吃饭");
  }
  public void walk(int distance){
    System.out.println("走路,走的距离是:" + distance + "公里");
    show();
    eat();
  }
  
  private void show(){
    System.out.println("我是一个人");
  }
  
  public Object info(){
    return null;
  }
  
  public double info1(){
    return 1.0;
  }

}      

Student类

public class Student extends Person{

  String major;
  
  public Student(){
    
  }
  public Student(String major){
    this.major = major;
  }
  
  public void study(){
    System.out.println("学习。专业是:" + major);
  }
  
  //对父类中的eat()进行了重写
  public void eat(){
    System.out.println("学生应该多吃有营养的食物");
  }
  
  public void show(){
    System.out.println("我是一个学生");
  }
  
  public String info(){
    return null;
  }
  
//  public int info1(){ //返回值类型不同,报错
//    return 1;
//  }
    
  //可以直接将父类的方法的第一行粘过来,直接写方法体
//  public void walk(int distance){
//    System.out.println("重写的方法");
//  }
  
  public void walk(int distance) {
    System.out.println("重写的方法");
  }

}      

测试类

public class PersonTest {
  
  public static void main(String[] args) {
    
    Student s = new Student("计算机科学与技术");
    s.eat();
    s.walk(10);
    
    System.out.println("**************");
    
    s.study();
    
    Person p1 = new Person();
    p1.eat();
    
  }
}      

2.1 方法重写的练习

练习1
1.如果将父类中的一个方法定义成private访问权限,在子类中将此方法声明为default访问权限,那么这样还叫重写吗?(NO)      
练习2

ManKind类

/*
 * 定义一个ManKind类,包括成员变量int sex和int salary;
 * 方法void manOrWoman():根据sex的值显示“man”(sex==1)或者“woman”(sex==0);
 * 方法void employeed():
 *     根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。
 */
public class ManKind {
  
  private int sex;//性别
  private int salary;//薪资
  
  public ManKind() {
  }
  
  public ManKind(int sex, int salary) {
    this.sex = sex;
    this.salary = salary;
  }

  public void manOrWoman(){
    if(sex == 1){
      System.out.println("man");
    }else if(sex == 0){
      System.out.println("woman");
    }
  }
  
  public void employeed(){
//    if(salary == 0){
//      System.out.println("no job");
//    }else{
//      System.out.println("job");
//    }
    //或
    String jobInfo = (salary == 0)? "no job" : "job";
    System.out.println(jobInfo);
  }

  public int getSex() {
    return sex;
  }

  public void setSex(int sex) {
    this.sex = sex;
  }

  public int getSalary() {
    return salary;
  }

  public void setSalary(int salary) {
    this.salary = salary;
  }
  
}      

Kids类

/*
 * 定义类Kids继承ManKind,并包括成员变量int yearsOld;
 * 方法printAge()打印yearsOld的值。
 */
public class Kids extends ManKind{
  private int yearsOld;
  
  public Kids() {
  }

  public Kids(int yearsOld) {
    this.yearsOld = yearsOld;
  }

  public void printAge(){
    System.out.println("I am " + yearsOld + " years old.");
    
  }

  public int getYearsOld() {
    return yearsOld;
  }

  public void setYearsOld(int yearsOld) {
    this.yearsOld = yearsOld;
  }
  
  /*
   * 在Kids中重新定义employeed()方法,
   * 覆盖父类ManKind中定义的employeed()方法,
   * 输出“Kids should study and no job.”
   */
  public void employeed() {
    System.out.println("Kids should study and no job.");
  }
}      

测试类

/*
 * 定义类KidsTest,在类的main方法中实例化Kids的对象someKid,
 * 用该对象访问其父类的成员变量及方法。
 */
public class KidsTest {
  public static void main(String[] args) {
    
    Kids someKid = new Kids(12);
    
    someKid.printAge();
    
    someKid.setSalary(0);
    someKid.setSex(1);
    
    someKid.employeed();
    someKid.manOrWoman();
  }
}      

3.四种访问权限修饰符

对于3.1的四种访问权限修饰符的补充
三篇文章彻底搞懂Java面向对象之二

cn.sxt.java2包下的Order类

/*
 * 体会4种不同的权限修饰
 */
public class Order {
  
  private int orderPrivate; //private
  int orderDefault; //default
  protected int orderProtected; //protected
  public int orderPublic; //public
  
  private void methodPrivate(){
    orderPrivate = 1;
    orderDefault = 2;
    orderProtected = 3;
    orderPublic = 4;
  }
  void methodDefault(){
    orderPrivate = 1;
    orderDefault = 2;
    orderProtected = 3;
    orderPublic = 4;
  }
  protected void methodProtected(){
    orderPrivate = 1;
    orderDefault = 2;
    orderProtected = 3;
    orderPublic = 4;
  }
  
  public void methodPublic(){
    orderPrivate = 1;
    orderDefault = 2;
    orderProtected = 3;
    orderPublic = 4;
  }
}      

cn.sxt.java2包下的OrderTest类

public class OrderTest {
  public static void main(String[] args) {
    Order order = new Order();
    
    order.orderDefault = 1;
    order.orderProtected = 2;
    order.orderPublic = 3;
    
    order.methodDefault();
    order.methodProtected();
    order.methodPublic();
    
    //同一个包中的其他类,不可以调用Order类中私有的属性、方法
//    order.orderPrivate = 4;
//    order.methodPrivate();
  }
}      
三篇文章彻底搞懂Java面向对象之二

cn.sxt.java3包下的SubOrder类

public class SubOrder extends Order {
  
  public void method(){
    orderProtected = 1;
    orderPublic = 2;
    
    methodProtected();
    methodPublic();
    
    //在不同包的子类中,不能调用Order类中声明为private和缺省(default)权限的属性、方法
//    orderDefault = 3;
//    orderPrivate = 4;
//    
//    methodDefault();
//    methodPrivate();
  }
  
}      

cn.sxt.java3包下的OrderTest类

public class OrderTest {
  public static void main(String[] args) {
    
    Order order = new Order();
    order.orderPublic = 1;
    order.methodPublic();
    
    //不同包下的普通类(非子类)要使用Order类,不可以调用声明为private、缺省(default)、protected权限的属性、方法
//    order.orderPrivate = 2;
//    order.orderDefault = 3;
//    order.orderProtected = 4;
//    
//    order.methodPrivate();
//    order.methodDefault();
//    order.methodProtected();
  }
  
  public void show(Order order){
    order.orderPublic = 1;
    order.methodPublic();
    
    //不同包下的普通类(非子类)要使用Order类,不可以调用声明为private、缺省、protected权限的属性、方法
//    order.orderPrivate = 2;
//    order.orderDefault = 3;
//    order.orderProtected = 4;
//    
//    order.methodPrivate();
//    order.methodDefault();
//    order.methodProtected();
//    
  }
}      

4.关键字:super

/*
 * 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(形参列表)",调用父类中的构造器
 */      

Person类

public class Person {
  String name;
  int age;
  int id = 1001;//身份证号
  
  public Person(){
    System.out.println("我无处不在!");
  }
  
  public Person(String name){
    this.name = name;
  }
  
  public Person(String name,int age){
    this(name);
    this.age = age;
  }
  
  public void eat(){
    System.out.println("人:吃饭");
  }
  public void walk(){
    System.out.println("人:走路");
  }
}      

Student类

public class Student extends Person{
  
  String major;
  int id = 1002;//学号
  
  public Student(){
    super();
  }
  
  public Student(String major) {
  super();
  this.major = major;
}
  
  public Student(String name,int age,String major){
//    this.name = name;
//    this.age = age;
    super(name,age);
    this.major = major;
  }
  
  @Override
  public void eat() {
    System.out.println("学生:多吃有营养的食物");
  }
  
  public void study(){
    System.out.println("学生:学习知识");
    this.eat();
    super.eat();
    walk();
  }
  
  public void show(){
    System.out.println("name = " + name + ", age = " + age);
    System.out.println("id = " + this.id);
    System.out.println("id = " + super.id);
  }
}      

测试类

/*
 * 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(形参列表)",调用父类中的构造器
 */
public class SuperTest {
  public static void main(String[] args) {
    
    Student s = new Student();
    s.show();
    
    System.out.println();
    
    s.study();
    
    Student s1 = new Student("Tom", 21, "IT");
    s1.show();
    
    System.out.println("************");
    Student s2 = new Student();
    
  }
}      

5.子类对象实例化的过程

class Creature { 
  
  public Creature() { 
    System.out.println("Creature无参数的构造器"); 
  }
  
}

class Animal extends Creature { 
  
  public Animal(String name) { 
    System.out.println("Animal带一个参数的构造器,该动物的name为" + name); 
  } 
  
  public Animal(String name, int age) {
    this(name); 
    System.out.println("Animal带两个参数的构造器,其age为" + age); 
  }
  
}

public class Wolf extends Animal { 
  
  public Wolf() { 
    super("灰太狼", 3); 
    System.out.println("Wolf无参数的构造器"); 
  } 
  
  public static void main(String[] args) { 
    new Wolf(); 
  }
  
}      
三篇文章彻底搞懂Java面向对象之二
三篇文章彻底搞懂Java面向对象之二
/*
 * 子类对象实例化的全过程
 * 
 * 1. 从结果上来看:(继承性)
 *     子类继承父类以后,就获取了父类中声明的属性或方法。
 *      创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
 * 
 * 2. 从过程上来看:
 *     当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,
 *      进而调用父类的父类的构造器,...
 *      直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,
 *      所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
 *    
 * 明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
 * 
 */      

4.1 super练习

/*
 * 写一个用户程序测试Account类。在用户程序中,
 * 创建一个账号为1122、余额为20000、年利率4.5%的Account对象。
 * 使用withdraw方法提款30000元,并打印余额。
 * 再使用withdraw方法提款2500元,
 * 使用deposit方法存款3000元,然后打印余额和月利率。
 */      

Account类

public class Account {
  private int id;//账号
  private double balance;//余额
  private double annualInterestRate;//年利率
  
  public Account(int id, double balance, double annualInterestRate) {
    super();
    this.id = id;
    this.balance = balance;
    this.annualInterestRate = annualInterestRate;
  }

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public double getBalance() {
    return balance;
  }

  public void setBalance(double balance) {
    this.balance = balance;
  }

  public double getAnnualInterestRate() {
    return annualInterestRate;
  }

  public void setAnnualInterestRate(double annualInterestRate) {
    this.annualInterestRate = annualInterestRate;
  }
  //返回月利率
  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类

public class AccountTest {
  public static void main(String[] args) {
    
    Account acct = new Account(1122, 20000, 0.045);
    
    acct.withdraw(30000);
    System.out.println("您的账户余额为:" + acct.getBalance());
    acct.withdraw(2500);
    System.out.println("您的账户余额为:" + acct.getBalance());
    acct.deposit(3000);
    System.out.println("您的账户余额为:" + acct.getBalance());
    
    System.out.println("月利率为:" + (acct.getMonthlyInterest() * 100) +"%");
    
  }
}      
/*
 * 创建Account类的一个子类CheckAccount代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额。
 * 在CheckAccount类中重写withdraw方法,其算法如下:
 *   如果(取款金额<账户余额),
 *     可直接取款
 *   如果(取款金额>账户余额),
 *   计算需要透支的额度
 *   判断可透支额overdraft是否足够支付本次透支需要,如果可以
 *     将账户余额修改为0,冲减可透支金额
 *   如果不可以
 *     提示用户超过可透支额的限额
 */      

CheckAccount类

public class CheckAccount extends Account{
  
  private double overdraft;//可透支限额
  
  public CheckAccount(int id, double balance, double annualInterestRate,double overdraft){
    super(id, balance, annualInterestRate);
    this.overdraft = overdraft;
    
  }
    
  public double getOverdraft() {
    return overdraft;
  }

  public void setOverdraft(double overdraft) {
    this.overdraft = overdraft;
  }

  @Override
  public void withdraw(double amount) {
    if(getBalance() >= amount){//余额就足够消费
//      getBalance() -= amount;
      //方式一:
//      setBalance(getBalance() - amount); 
      //方式二:
      super.withdraw(amount);
    }else if(overdraft >= amount - getBalance()){//透支额度+余额足够消费
      
      overdraft -= (amount - getBalance());
      
//      setBalance(0);
      //或
      super.withdraw(getBalance());
      
    }else{
      System.out.println("超过可透支限额!");
    }
  }

}      
/*
 * 写一个用户程序测试CheckAccount类。
 * 在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象。
 * 使用withdraw方法提款5000元,并打印账户余额和可透支额。
 * 再使用withdraw方法提款18000元,并打印账户余额和可透支额。
 * 再使用withdraw方法提款3000元,并打印账户余额和可透支额。
 */      

CheckAccountTest类

public class CheckAccountTest {
  public static void main(String[] args) {
    
    CheckAccount acct = new CheckAccount(1122, 20000, 0.045, 5000);
    
    acct.withdraw(5000);
    System.out.println("您的账户余额为:" + acct.getBalance());
    System.out.println("您的可透支额度为:" + acct.getOverdraft());
    acct.withdraw(18000);
    System.out.println("您的账户余额为:" + acct.getBalance());
    System.out.println("您的可透支额度为:" + acct.getOverdraft());
    acct.withdraw(3000);
    System.out.println("您的账户余额为:" + acct.getBalance());
    System.out.println("您的可透支额度为:" + acct.getOverdraft());
    
  }
}      

6.面向对象的特征之三: 多态性

/*
 * 面向对象特征之三:多态性
 * 
 * 1.理解多态性:可以理解为一个事物的多种形态。
 * 2.何为多态性:
 *   对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
 *   
 * 3. 多态的使用:虚拟方法调用
 *   有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
 *   总结:编译,看左边;    运行,看右边。
 * 
 *  若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
 *  多态情况下,
 *   “看左边”:看的是父类的引用(父类中不具备子类特有的方法)
 *   “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
 *   
 * 4.多态性的使用前提:  ① 类的继承关系  ② 方法的重写
 * 
 * 5.对象的多态性:只适用于方法(编译看左边,运行看右边),不适用于属性(编译和运行都看左边)
 */      

Person类

public class Person {
  String name;
  int age;
  
  int id = 1001;
  
  public void eat(){
    System.out.println("人:吃饭");
  }
  
  public void walk(){
    System.out.println("人:走路");
  }
  
}      

Woman类

public class Woman extends Person{
  
  boolean isBeauty;
  
  public void goShopping(){
    System.out.println("女人喜欢购物");
  }
  
  public void eat(){
    System.out.println("女人少吃,为了减肥");
  }
  
  public void walk(){
    System.out.println("女人窈窕地走路");
  }
}      

Man类

public class Man extends Person{
  
  boolean isSmoking;
  
  int id = 1002;
  
  public void earnMoney(){
    System.out.println("男人负责挣钱养家");
  }
  
  public void eat(){
    System.out.println("男人多吃肉,长肌肉");
  }
  
  public void walk(){
    System.out.println("男人霸气的走路");
  }

}      

测试类

public class PersonTest {
  
  public static void main(String[] args) {
    
    Person p1 = new Person();
    p1.eat();
    
    Man man = new Man();
    man.eat();
    man.age = 25;
    man.earnMoney();
    
    //*************************************************
    System.out.println("*******************");
    //对象的多态性:父类的引用指向子类的对象
    Person p2 = new Man();
//    Person p3 = new Woman();
    //多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
    p2.eat();
    p2.walk();
    
//    p2.earnMoney();
    
    System.out.println(p2.id);//1001
    
  }
}      
多态性的使用举例
//多态性的使用举例一:
public class AnimalTest {
  
  public static void main(String[] args) {
    
    AnimalTest test = new AnimalTest();
    test.func(new Dog());
    
    test.func(new Cat());
  }
  
  public void func(Animal animal){//Animal animal = new Dog();
    animal.eat();
    animal.shout();
    
    if(animal instanceof Dog){
      Dog d = (Dog)animal;
      d.watchDoor();
    }
  }
  
//  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{
  public void eat(){
    System.out.println("狗吃骨头");
  }
  
  public void shout(){
    System.out.println("汪!汪!汪!");
  }
  
  public void watchDoor(){
    System.out.println("看门");
  }
}
class Cat extends Animal{
  public void eat(){
    System.out.println("猫吃鱼");
  }
  
  public void shout(){
    System.out.println("喵!喵!喵!");
  }
}

//举例二:

class Order{
  
  public void method(Object obj){
    
  }
}

//举例三:
class Driver{
  
  public void doData(Connection conn){//conn = new MySQlConnection(); / conn = new OracleConnection();
    //规范的步骤去操作数据
//    conn.method1();
//    conn.method2();
//    conn.method3();
    
  }
  
}      

6.1 虚拟方法的补充

import java.util.Random;
/*
 * 2.从编译和运行的角度看:
 * 重载,是指允许存在多个同名方法,而这些方法的参数不同。
 * 编译器根据方法不同的参数表,对同名方法的名称做修饰。
 * 对于编译器而言,这些同名方法就成了不同的方法。
 * 它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,
 * 即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
 * 
 * 而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
 * 
 * 引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
 */
//面试题:多态是编译时行为还是运行时行为?
//证明如下:
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();
     
  }

}      

6.2 向下转型的使用

Person类

public class Person {
  String name;
  int age;
  
  int id = 1001;
  
  public void eat(){
    System.out.println("人:吃饭");
  }
  
  public void walk(){
    System.out.println("人:走路");
  }
  
}      

Man类

public class Man extends Person{
  
  boolean isSmoking;
  
  int id = 1002;
  
  public void earnMoney(){
    System.out.println("男人负责挣钱养家");
  }
  
  public void eat(){
    System.out.println("男人多吃肉,长肌肉");
  }
  
  public void walk(){
    System.out.println("男人霸气的走路");
  }

}      

Woman类

public class Woman extends Person{
  
  boolean isBeauty;
  
  public void goShopping(){
    System.out.println("女人喜欢购物");
  }
  
  public void eat(){
    System.out.println("女人少吃,为了减肥");
  }
  
  public void walk(){
    System.out.println("女人窈窕的走路");
  }
}      

PersonTest类

/*
 * 面向对象特征之三:多态性
 * 
 * 1.理解多态性:可以理解为一个事物的多种形态。
 * 2.何为多态性:
 *   对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
 *   
 * 3. 多态的使用:虚拟方法调用
 *   有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
 *   总结:编译,看左边;    运行,看右边。
 * 
 *  若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
 *  多态情况下,
 *   “看左边”:看的是父类的引用(父类中不具备子类特有的方法)
 *   “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
 *   
 * 4.多态性的使用前提:  ① 类的继承关系  ② 方法的重写
 * 
 * 5.对象的多态性:只适用于方法(编译看左边,运行看右边),不适用于属性(编译和运行都看左边)
 */
public class PersonTest {
    public static void main(String[] args) {
        
        Person p1 = new Person();
        p1.eat();
        
        Man man = new Man();
        man.eat();
        man.age = 25;
        man.earnMoney();
        
        //*************************************************
        System.out.println("*******************");
        //对象的多态性:父类的引用指向子类的对象
        Person p2 = new Man();
//      Person p3 = new Woman();
        //多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
        p2.eat();
        p2.walk();
        
//      p2.earnMoney();
        
        System.out.println(p2.id);//1001
        
        System.out.println("****************************");
        //不能调用子类所特有的方法、属性:编译时,p2是Person类型。
        p2.name = "Tom";
//      p2.earnMoney();
//      p2.isSmoking = true;
        //有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,
        //但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。
        //子类特有的属性和方法不能调用。
        
        //如何才能调用子类特有的属性和方法?
        //向下转型:使用强制类型转换符。
        Man m1 = (Man)p2;
        m1.earnMoney();
        m1.isSmoking = true;
        
        //使用强转时,可能出现ClassCastException的异常。
//      Woman w1 = (Woman)p2;
//      w1.goShopping();
        
        /*
         * instanceof关键字的使用
         * 
         * a instanceof A:判断对象a是否是类A的实例,或者是类A的直接或间接子类。如果是,返回true;如果不是,返回false。
         * 
         *  使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先
         *  进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
         *  
         *  如果 a instanceof A返回true,a instanceof B也返回true
         *  那么A可能是B的父类,或者说B可能是A的父类
         */
        if(p2 instanceof Woman){
            Woman w1 = (Woman)p2;
            w1.goShopping();
            System.out.println("******Woman******");
        }
        
        if(p2 instanceof Man){
            Man m2 = (Man)p2;
            m2.earnMoney();
            System.out.println("******Man******");
        }
        
        if(p2 instanceof Person){
            System.out.println("******Person******");
        }
        if(p2 instanceof Object){
            System.out.println("******Object******");
        }
        
//      if(p2 instanceof String){
//          
//      }
        
    //向下转型的常见问题
        //练习:
        //问题一:编译时通过,运行时不通过
        //举例一:
//      Person p3 = new Woman();
//      Man m3 = (Man)p3;
        //举例二:
//      Person p4 = new Person();
//      Man m4 = (Man)p4;

        
        //问题二:编译通过,运行时也通过
//      Object obj = new Woman();
//      Person p = (Person)obj;
        
        //问题三:编译不通过
//      Man m5 = new Woman();
        
//      String str = new Date();
        
//      Object o = new Date();
//      String str1 = (String)o;
        
    }
}      
三篇文章彻底搞懂Java面向对象之二

6.3 多态性的练习

练习一

/*
 * 练习:
 * 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,
 * 系统将不可能把父类里的方法转移到子类中:编译看左边,运行看右边
 * 
 * 2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,
 * 这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边
 * 
 */
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);
  }
}

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

练习二

/*
 * 建立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 test = new InstanceTest();
    test.method(new Student());
  }
  
  
  public void method(Person e){
    
    //虚拟方法调用
    String info = e.getInfo();
    System.out.println(info);
    
    //方式一
//    if(e instanceof Graduate){
//      System.out.println("a graduated student");
//      System.out.println("a student");
//      System.out.println("a person");
//    }else if(e instanceof Student){
//      System.out.println("a student");
//      System.out.println("a person");
//    }else{
//      System.out.println("a person");
//    }
    
    //方式二
    if(e instanceof Graduate){
      System.out.println("a graduated student");
    }
    
    if(e instanceof Student){ //Graduate的父类
      System.out.println("a student");
    }
    
    if(e instanceof Person){ //Student的父类
      System.out.println("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;
  }
}      

练习三

GeometricObject类

/*
 * 定义三个类,父类GeometricObject代表几何形状,子类Circle代表圆形,MyRectangle代表矩形。
 */
public class GeometricObject {//几何图形

  protected String color;
  protected double 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 GeometricObject(String color, double weight) {
    super();
    this.color = color;
    this.weight = weight;
  }
  
  public double findArea(){
    return 0.0;
  }
  
}      

Circle类

public class Circle extends GeometricObject {

  private double 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;
  }

}      

MyRectangle类

public class MyRectangle extends GeometricObject {

  private double width;
  private double height;
  
  public MyRectangle(double width,double height,String color, double weight) {
    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;
  }
}      

GeometricTest类

/*
 * 定义一个测试类GeometricTest,
 * 编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术),
 * 编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
 */
public class GeometricTest {

  public static void main(String[] args) {
    GeometricTest test = new GeometricTest();
    
    Circle c1 = new Circle(3.3, "white", 1.0);
    test.displayGeometricObject(c1);
    Circle c2 = new Circle(3.3, "white", 1.0);
    test.displayGeometricObject(c2);
    
    boolean isEquals = test.equalsArea(c1, c2);
    System.out.println("c1 和 c2的面积是否相等:" + isEquals);
    
    MyRectangle rect = new MyRectangle(2.1, 3.4, "red", 2.0);
    test.displayGeometricObject(rect);
    
  }
  
  public void displayGeometricObject(GeometricObject o){//GeometricObject o = new Circle(...)
    System.out.println("面积为:" + o.findArea());
  }
  
  //测试两个对象的面积是否相等
  public boolean equalsArea(GeometricObject o1,GeometricObject o2){
    return o1.findArea() == o2.findArea();
  }
}      

练习4

/*
 * 面试题:多态是编译时行为还是运行时行为?如何证明?
 * 
 * 证明见如下:
 */
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();
     
  }
}      

面试题拓展

/* 考查多态的笔试题目:
 * 面试题:多态是编译时行为还是运行时行为?如何证明?
 * 
 * 拓展问题
 */
public class InterviewTest1 {

  public static void main(String[] args) {
    Base1 base = new Sub1();
    base.add(1, 2, 3); //编译时无法确定到底是调用哪个方法

//    Sub1 s = (Sub1)base;
//    s.add(1,2,3);
  }
}

class Base1 {
  public void add(int a, int... arr) {
    System.out.println("base1");
  }
}

class Sub1 extends Base1 {

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

}      

6.4 Object类的使用

/*
 * java.lang.Object类
 * 1.Object类是所有Java类的根父类
 * 2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类 
 * 3.Object类中的功能(属性、方法)就具有通用性。
 *   属性:无
 *  方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()
 *     wait() 、 notify()、notifyAll()
 * 
 * 4. Object类只声明了一个空参的构造器
 * 
 * 面试题:
 * final、finally、finalize的区别?
 */
public class ObjectTest {

  public static void main(String[] args) {
    
    Order order = new Order();
    System.out.println(order.getClass().getSuperclass());
    
  }
}

class Order{
  
}      

6.4.1 Object类中的主要结构

三篇文章彻底搞懂Java面向对象之二

6.4.2 ==操作符与equals方法

/*
 * 面试题: == 和 equals() 区别
 * 
 * 一、回顾 == 的使用:
 * == :运算符
 * 1. 可以使用在基本数据类型变量和引用数据类型变量中
 * 2. 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
 *    如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
 * 补充: == 符号使用时,必须保证符号左右两边的变量类型一致。
 * 
 * 二、equals()方法的使用:
 * 1. 是一个方法,而非运算符
 * 2. 只能适用于引用数据类型
 * 3. Object类中equals()的定义:
 *    public boolean equals(Object obj) {
          return (this == obj);
    }
 *    说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
 * 
 * 4. 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是
 *    两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
 *    
 * 5. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们
 *    就需要对Object类中的equals()进行重写.
 *    重写的原则:比较两个对象的实体内容是否相同.
 */
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 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);//true
    
    //引用类型:
    Customer cust1 = new Customer("Tom",21);
    Customer cust2 = new Customer("Tom",21);
    System.out.println(cust1 == cust2);//false
    
    String str1 = new String("atguigu");
    String str2 = new String("atguigu");
    System.out.println(str1 == str2);//false
    System.out.println("****************************");
    System.out.println(cust1.equals(cust2));//false--->true
    System.out.println(str1.equals(str2));//true
    
    Date date1 = new Date(32432525324L);
    Date date2 = new Date(32432525324L);
    System.out.println(date1.equals(date2));//true
  }
}      

Customer类

public class Customer {
  
  private String name;
  private int age;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  public Customer() {
    super();
  }
  public Customer(String name, int age) {
    super();
    this.name = name;
    this.age = age;
  }

  //自动生成的equals()
  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    Customer other = (Customer) obj;
    if (age != other.age)
      return false;
    if (name == null) {
      if (other.name != null)
        return false;
    } else if (!name.equals(other.name))
      return false;
    return true;
  }
  
  //重写的原则:比较两个对象的实体内容(即:name和age)是否相同
  //手动实现equals()的重写
//  @Override
//  public boolean equals(Object obj) {
//    
    System.out.println("Customer equals()....");
//    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 this.age == cust.age && this.name.equals(cust.name);
//    }else{
//      return false;
//      
//    }
//    
//  }
  //手动实现
//  @Override
//  public String toString() {
//    return "Customer[name = " + name + ",age = " + age + "]"; 
//  }
  //自动实现
  @Override
  public String toString() {
    return "Customer [name=" + name + ", age=" + age + "]";
  }
}      

6.4.3 重写equals( )方法的原则

/*
1.对称性 : 如果 x.equals(y) 返回是 “ true ” , 那么 y.equals(x) 也应该返回是 “true”。 
2.自反性:x.equals(x)必须返回是“true”。 
3.传递性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”, 那么z.equals(x)也应该返回是“true”。 
4.一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你 重复x.equals(y)多少次,返回都是“true”。 
5.任何情况下,x.equals(null),永远返回是“false”; x.equals(和x不同类型的对象)永远返回是“false”。
*/

  int it = 65;
  float fl= 65.0f;
  System.out.println("65和65.0f是否相等?" + (it == fl)); //true
  char ch1 = 'A'; 
  char ch2 = 12;
  System.out.println("65和'A'是否相等?" + (it == ch1));//true
  System.out.println("12和ch2是否相等?" + (12 == ch2));//true 
  String str1 = new String("hello");
  String str2 = new String("hello");
  System.out.println("str1和str2是否相等?"+ (str1 == str2));//false
  System.out.println("str1是否equals str2?"+(str1.equals(str2)));//true
  System.out.println("hello" == new java.util.Date()); //编译不通过      
练习

练习一

/*
 * 编写Order类,有int型的orderId,String型的orderName,
 * 相应的getter()和setter()方法,两个参数的构造器,
 * 重写父类的equals()方法:public boolean equals(Object obj),
 * 并判断测试类中创建的两个对象是否相等。
 */
public class OrderTest {
  public static void main(String[] args) {
    Order order1 = new Order(1001, "AA");
    Order order2 = new Order(1001, new String("BB"));
    
    System.out.println(order1.equals(order2));
    
    Order order3 = new Order(1001, "BB");
    System.out.println(order2.equals(order3));
    
//    String s1 = "BB";
//    String s2 = "BB";
//    System.out.println(s1 == s2);//true
    
  }
}

class Order{
  private int orderId;
  private String orderName;
  
  public int getOrderId() {
    return orderId;
  }
  public void setOrderId(int orderId) {
    this.orderId = orderId;
  }
  public String getOrderName() {
    return orderName;
  }
  public void setOrderName(String orderName) {
    this.orderName = orderName;
  }
  public Order(int orderId, String orderName) {
    super();
    this.orderId = orderId;
    this.orderName = orderName;
  }
  
  @Override
  public boolean equals(Object obj) {
    if(this == obj){
      return true;
    }
    
    if(obj instanceof Order){
      Order order = (Order)obj;
      //正确的:
      return this.orderId == order.orderId && 
          this.orderName.equals(order.orderName);
      //错误的:
//      return this.orderId == order.orderId &&
//          this.orderName == order.orderName;
    }
    
    return false;
  }
}      

练习二

/*
 * 请根据以下代码自行定义能满足需要的MyDate类,在MyDate类中覆盖equals方法,
 * 使其判断当两个MyDate类型对象的年月日都相同时,结果为true,否则为false。
 * public boolean equals(Object o)
 */
public class MyDateTest {
    public static void main(String[] args) {
        MyDate m1 = new MyDate(14, 3, 1976);
        MyDate m2 = new MyDate(14, 3, 1976);
        if (m1 == m2) {
            System.out.println("m1==m2");
        } else {
            System.out.println("m1!=m2"); // m1 != m2
        }

        if (m1.equals(m2)) {
            System.out.println("m1 is equal to m2");// m1 is equal to m2
        } else {
            System.out.println("m1 is not equal to m2");
        }
    }

}

class MyDate{
    private int day;
    private int month;
    private int year;
    
    public MyDate(int day, int month, int year) {
        super();
        this.day = day;
        this.month = month;
        this.year = year;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    @Override
    public boolean equals(Object obj) {
        if(this == obj){
            return true;
        }
        
        if(obj instanceof MyDate){
            MyDate myDate = (MyDate)obj;
            return this.day == myDate.day && this.month == myDate.month &&
                    this.year == myDate.year;
        }
        
        return false;

    }

//  @Override
//  public boolean equals(Object obj) {
//      if (this == obj)
//          return true;
//      if (obj == null)
//          return false;
//      if (getClass() != obj.getClass())
//          return false;
//      MyDate other = (MyDate) obj;
//      if (day != other.day)
//          return false;
//      if (month != other.month)
//          return false;
//      if (year != other.year)
//          return false;
//      return true;
//  }
    
}      

6.4.4 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()方法,当调用此方法时,返回对象的"实体内容"
 */      

Customer类

public class Customer {
  
  private String name;
  private int age;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  public Customer() {
    super();
  }
  public Customer(String name, int age) {
    super();
    this.name = name;
    this.age = age;
  }

  //自动生成的equals()
  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    Customer other = (Customer) obj;
    if (age != other.age)
      return false;
    if (name == null) {
      if (other.name != null)
        return false;
    } else if (!name.equals(other.name))
      return false;
    return true;
  }
    
  //重写的原则:比较两个对象的实体内容(即:name和age)是否相同
  //手动实现equals()的重写
//  @Override
//  public boolean equals(Object obj) {
//    
    System.out.println("Customer equals()....");
//    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 this.age == cust.age && this.name.equals(cust.name);
//    }else{
//      return false;
//      
//    }
//    
//  }
  //手动实现
//  @Override
//  public String toString() {
//    return "Customer[name = " + name + ",age = " + age + "]"; 
//  }
  //自动实现
  @Override
  public String toString() {
    return "Customer [name=" + name + ", age=" + age + "]";
  }
}      

ToStringTest类

/*
 * 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()方法,当调用此方法时,返回对象的"实体内容"
 */
public class ToStringTest {
  public static void main(String[] args) {
    
    Customer cust1 = new Customer("Tom",21);
    System.out.println(cust1.toString());//com.atguigu.java1.Customer@15db9742-->Customer[name = Tom,age = 21]
    System.out.println(cust1);//com.atguigu.java1.Customer@15db9742-->Customer[name = Tom,age = 21]
    
    String str = new String("MM");
    System.out.println(str);//MM
    
    Date date = new Date(4534534534543L);
    System.out.println(date.toString());//Mon Sep 11 08:55:34 GMT+08:00 2113
    
  }
}      
练习
/*
 * 写一个测试类,创建两个Circle对象,判断其颜色是否相等;
 * 利用equals方法判断其半径是否相等;利用toString()方法输出其半径。
 */      

GeometricObject类

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

Circle类

public class Circle extends GeometricObject {
  
  private double radius;

  public Circle() {
    super();
    radius = 1.0;
//    color = "white";
//    weight = 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;
  }
  //比较两个圆的半径是否相等,如相等,返回true。
  @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 + "]";
  }
  
}      

测试类

/*
 * 写一个测试类,创建两个Circle对象,判断其颜色是否相等;
 * 利用equals方法判断其半径是否相等;利用toString()方法输出其半径。
 */
public class CircleTest {
  public static void main(String[] args) {
    
    Circle circle1 = new Circle(2.3);
    Circle circle2 = new Circle(3.3, new String("white"), 2.0);
    
    System.out.println("颜色是否相等:" + circle1.getColor().equals(circle2.getColor()));
    
    System.out.println("半径是否相等:" + circle1.equals(circle2));
    
    System.out.println(circle1);
    System.out.println(circle2.toString());
    
  }
}      

6.5 包装类的使用

6.5.1 单元测试方法的使用

/*
 * Java中的JUnit单元测试
 * 
 * 步骤:
 * 1.选中当前工程 - 右键选择:build path - add libraries - JUnit 4 - 下一步
 * 2.创建Java类,进行单元测试。
 *   此时的Java类要求:① 此类是public的  ②此类提供公共的无参的构造器
 * 3.此类中声明单元测试方法。
 *   此时的单元测试方法:方法的权限是public,没有返回值,没有形参
 * 
 * 4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
 * 
 * 5.声明好单元测试方法以后,就可以在方法体内测试相关的代码。
 * 6.写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Test
 * 
 * 说明:
 * 1.如果执行结果没有任何异常:绿条
 * 2.如果执行结果出现异常:红条
 */
public class JUnitTest {
  
  int num = 10;
  
  @Test
  public void testEquals(){
    String s1 = "MM";
    String s2 = "MM";
    System.out.println(s1.equals(s2));
    //ClassCastException的异常
//    Object obj = new String("GG");
//    Date date = (Date)obj;
    
    System.out.println(num);
    show();
  }
  
  public void show(){
    num = 20;
    System.out.println("show()....");
  }
  
  @Test
  public void testToString(){
    String s2 = "MM";
    System.out.println(s2.toString());
  }
}      
三篇文章彻底搞懂Java面向对象之二
三篇文章彻底搞懂Java面向对象之二

6.5.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
 * /      

6.5.3 包装类与基本数据类型相互转换

三篇文章彻底搞懂Java面向对象之二
/*
 * 包装类的使用:
 * 1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
 * 
 * 2.掌握的:基本数据类型、包装类、String三者之间的相互转换
 */
public class WrapperTest {
  
  //String类型 --->基本数据类型、包装类:调用包装类的parseXxx(String s)
  @Test
  public void test5(){
    String str1 = "123";
    //错误的情况:
//    int num1 = (int)str1;
//    Integer in1 = (Integer)str1;
    //可能会报NumberFormatException
    int num2 = Integer.parseInt(str1);
    System.out.println(num2 + 1);
    
    String str2 = "true1";
    boolean b1 = Boolean.parseBoolean(str2);
    System.out.println(b1); //false
  }
  
  //基本数据类型、包装类--->String类型:调用String重载的valueOf(Xxx xxx)
  @Test
  public void test4(){
    
    int num1 = 10;
    //方式1:连接运算
    String str1 = num1 + "";
    //方式2:调用String的valueOf(Xxx xxx)
    float f1 = 12.3f;
    String str2 = String.valueOf(f1);//"12.3"
    
    Double d1 = new Double(12.4);
    String str3 = String.valueOf(d1);
    System.out.println(str2);
    System.out.println(str3);//"12.4"
    
  }
  
  /*
   * JDK 5.0 新特性:自动装箱 与自动拆箱
   */
  @Test
  public void test3(){
//    int num1 = 10;
//    //基本数据类型-->包装类的对象
//    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);
  }
  
  //包装类--->基本数据类型:调用包装类Xxx的xxxValue()
  @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 test1(){
    
    int num1 = 10;
//    System.out.println(num1.toString()); //错误,基本数据类型没有方法
    Integer in1 = new Integer(num1);
    System.out.println(in1.toString());
    
    Integer in2 = new Integer("123");
    System.out.println(in2.toString());
    
    //报异常
//    Integer in3 = new Integer("123abc");
//    System.out.println(in3.toString());
    
    Float f1 = new Float(12.3f);
    Float f2 = new Float("12.3");
    System.out.println(f1);
    System.out.println(f2);
    
    Boolean b1 = new Boolean(true);
    Boolean b2 = new Boolean("TrUe");
    System.out.println(b2);//true,忽略大小写
    Boolean b3 = new Boolean("true123");
    System.out.println(b3);//false,和true不一样
    
    
    Order order = new Order();
    System.out.println(order.isMale);//false,默认为false
    System.out.println(order.isFemale);//null
  }
  
}

class Order{
  
  boolean isMale;
  Boolean isFemale;
}      

6.5.4 包装类练习

/*
 * 关于包装类使用的面试题
 */
public class InterviewTest {

  @Test
  public void test1() {
    Object o1 = true ? new Integer(1) : new Double(2.0);
    //三元运算符要求:   " : "两边的变量应该是同一类型,所以int类型自动提升为double类型
    System.out.println(o1);// 1.0
  }

  @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 test3() {
    Integer i = new Integer(1);
    Integer j = new Integer(1);
    System.out.println(i == j);//false,比较地址值
    
    //Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[]的数组,
    //保存了从-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在
    //-128~127范围内时,可以直接使用数组中的元素,不用再去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
  }

}      
/*
 *  利用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 ScoreTest {
  public static void main(String[] args) {
    //1.实例化Scanner,用于从键盘获取学生成绩
    Scanner scan = new Scanner(System.in);
    
    //2.创建Vector对象:Vector v=new Vector();相当于原来的数组
    Vector v = new Vector();
    
    //3.通过for(;;)或while(true)方式,给Vector中添加数组
    int maxScore = 0;
    for(;;){
      System.out.println("请输入学生成绩(以负数代表输入结束)");
      int score = scan.nextInt();
      //3.2 当输入是负数时,跳出循环
      if(score < 0){
        break;
      }
      if(score > 100){
        System.out.println("输入的数据非法,请重新输入");
        continue;
      }
      //3.1 添加操作::v.addElement(Object obj)
      //jdk5.0之前:
//      Integer inScore = new Integer(score);
//      v.addElement(inScore);//多态
      //jdk5.0之后:
      v.addElement(score);//自动装箱
      //4.获取学生成绩的最大值
      if(maxScore < score){
        maxScore = score;
      }
    }
    
    //5.遍历Vector,得到每个学生的成绩,并与最大成绩比较,得到每个学生的等级。
    char level;
    for(int i = 0;i < v.size();i++){
      Object obj = v.elementAt(i);
      //jdk 5.0之前:
//      Integer inScore = (Integer)obj;
//      int score = inScore.intValue();
      //jdk 5.0之后:
      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);
      
    }
  
  }
}