三大特性:(经典代码)

 

 1.定义一个Person类

public class Person {
    
    private String name;  // 姓名
    private int age;      // 年龄
    
    public Person() {    // 无参构造
        super();
    }
    public Person(String name, int age) {  // 有参构造
        super();
        this.name = name;
        this.age = 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;
    }
    
   
    
    @Override  // 重写toString()方法
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
    
    public void show() {
        System.out.println("Person类中的show()方法");
    }
    
    
    
}

 2.写一个Student 学生类

public class Student extends Person {
    private int id;   // 学号
    
    public Student() {  // 无参构造
        super();
    }

    public Student(String name, int age,int id) {   // 有参构造
        
        super(name,age);  // 调用父类中的构造方法
        this.id = id;
    }


     //提供供外界访问的 get/set()方法
    public int getId() {
        return id;
    }


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

     @Override
    public String toString() {
        return "Student [id=" + id +" name="+getName()+" age="+getAge()+ "]";
    }


    @Override
    public void show() {
        System.out.println("Student类中重写后的show()方法");
         
    }
}

 


 

3.写一个 Teacher 教师类

package com.monkey1024;

public class Teacher extends Person {
    
    private double salary;  // 工资

    public Teacher() {
        super();
    }

    public Teacher(String name, int age,double salary) {
        super(name, age);
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

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

    @Override
    public String toString() {
        return "Person [name=" + getName() + ", age=" + getAge() + ", salary="+salary+"]";
    }

    @Override
    public void show() {
         System.out.println("Teacher类中重写后的show()方法");
    }

}

 


 

测试类:

public class Test {

    public static void main(String[] args) {
        
        // 创建一个学生类
        Student student = new Student("小茅棚",23,01);   // 姓名,年龄,学号
        System.out.println(student);
        student.show();
        System.out.println("--------------------");
        
        // 创建一个教师类
        Teacher teacher = new Teacher("马云",55,88888.8); // 姓名, 年龄, 工资
        System.out.println(teacher);
        teacher.show();

    }

}

 

 

控制台结果:

Student [id=1 name=小茅棚 age=23]
Student类中重写后的show()方法
--------------------
Person [name=马云, age=55, salary=88888.8]
Teacher类中重写后的show()方法

 

重点:

子类继承父类,

若子类 没有重写 父类的方法,则 运行阶段 调用从父类中继承的对应方法  -----如该程序中的,show()方法

若子类 重写了父类中的方法, 则 运行阶段 调用自己重写之后的该方法。

 

注意:

无论是否重写父类的方法,在编译阶段 程序统一都是调用父类中的该方法。

(这就是为什么,虽然子类中对父类方法进行了重写,但是一旦删除父类中被重写的方法,程序立马报错的原因)

 


 测试类中写如下内容:

public class Test {
    
    // 定义一个静态的print()方法,既能打印人类的功能,又能打印老师的功能 还能能打印学生的功能
public static void print(Person p) { // 形参要的是是一个Person类型 p.show(); } public static void main(String[] args) { Test.print(new Person()); System.out.println("--------------"); Test .print(new Student()); System.out.println("--------------"); Test.print(new Teacher()); } }

 

 

控制台结果:

Person类中的show()方法
--------------
Student类中重写后的show()方法
--------------
Teacher类中重写后的show()方法

 

 public static void print(Person p) {      

该方法中要求的是一个 Person类型的引用作为方法的形参

在main()方法的代码中,把Person类的子类 Student类的引用 和 Teacher类的引用作为形参 传递 过去,并不报错。且运行阶段 调用的是他们各自的方法。

 


代码改进:

     如 Person类中,有 name 和 age属性,其中age代表的是年龄。

     生活经验告诉我们,年龄通常不能小于0,且通常不会超过150岁。

    但是在以上的代码中,将age设置为负数 或 888,程序也正常运行。这显然是不符合逻辑的。

 

改动代码:

在setAge()方法中 对传入的 age值做合理值 判断

 

 public void setAge(int age) {
        if(age>0 && age<=150) // 判断条件
        this.age = age;
        else
            System.out.println("年龄输入不符合常理....");
    }

 

 

在构造方法中将 this.age= age; 改为setAge(age);

 

public Person(String name, int age) {  // 有参构造
        super();
        this.name = name;
        // this.age = age;
        setAge(age);    
    }

 

测试:

System.out.println(new Person("james",888));

 

控制台打印结果:

年龄输入不符合常理....
Person [name=james, age=0]

 同理,像salary、id 等属性 都最好 传值前做合理值判断,来避免发生夸张不合常理的运算结果


让程序产生异常:

若传入不符合常理的age值,当然也可以使用暴力点的方式处理问题直接让程序产生异常     

 

1.先自定义一个异常:AgeException 年龄值异常

// 自定义一个异常: id异常
public class AgeException extends Exception {
     
    public AgeException() {   // 无参构造
        
    }
    
   public AgeException(String str) {   // 有参构造
        super(str);
    }
    
    

}

 

2.Person类中修改代码:setAge()方法

public void setAge(int age) throws Exception {
        if(age>0 && age<=150)
        this.age = age;
        else
            // System.out.println("年龄输入不符合常理....");
            throw new AgeException("年龄输入不符合常理.....");    // 这里指定要抛出AgeException异常类型
    }

 

 

3.将程序报警的每个地方进行,异常的抛出 或 异常的捕获 处理

 

4.test代码:

public class Test {
    
 public static void main(String[] args) throws Exception {    // 异常的向上抛出
     
     Student student = new Student("Bob",-1,1002);   // 年龄值 传递了一个 -1
     System.out.println(student);
 System.out.println("-------------"); }

 

控制台打印结果:

Exception in thread "main" com.monkey1024.AgeException: 年龄输入不符合常理.....
    at com.monkey1024.Person.setAge(Person.java:32)
    at com.monkey1024.Person.<init>(Person.java:15)
    at com.monkey1024.Student.<init>(Student.java:16)
    at com.monkey1024.Test.main(Test.java:7)

 

 

 

 

 

 


还有注意:

super();   // 写在子类构造方法体的首行,表示 调用父类中的无参构造

super(属性类型1,属性值1, 属性类型2 属性值2...);  //表示调用父类中对应的有参构造

super.父类方法();      // 写在子类重写之后的方法体内,表示 调用父类中的该方法

 

this关键字 表示 --"当前对象"    谁调用该方法,this 代表的就是 谁这个对象


 

多态:

            父类类型的引用 指向 子类类型的对象      ===>  形成多态!!!

 

 测试代码:

public class Test {
    
 public static void main(String[] args) {
     
     //Person类型的引用p 指向 Student类型的对象 形成 多态
     Person p = new Student();
     p.show();
     
     
    //Person类型的引用p 指向 Teacher类型的对象 形成 多态
     p = new Teacher();
     p.show();
     
}

}

 

 

控制台结果:
Student类中重写后的show()方法
Teacher类中重写后的show()方法

 

 

分析:

 p.show();

 虽然是Person类型的引用p调用show()方法,但是运行出来的结果表明:执行的是子类各自的重写之后的show()方法

 

其中:

            父类类型的引用 指向 子类的对象, 子类 is a 父类
           Person p = new Student();   此时,发生了 Student类型 向 Person类型的自动类型转换 

子类 is a 父类,父类有的方法,子类也一定可以调用。



产生问题:那么父类可以调用子类中的方法吗?

 Person ps = new Student("Bob",24,1002);
     ps.show();
     
     // ps.getId();       编译报错,是因为ps是Person类型的引用。父类类型的引用不能直接调用子类类型的方法
Student ts=(Student)ps; //做了强制类型转换:将Person类型的ps 强转为 Student类型 int res = ts.getId(); System.out.println(res); }

 

通过上面代码可以知道:
父类类型的引用 要想访问 子类特有的方法,则需要进行 强制类型转换才能访问。 (父类引用直接访问子类属性则编译报错)

再思考一问题:若把 Person类型的引用 强制 转换 为String类型,再调用String类中length()方法能不能成功?
res = ((String)ps).length();  // 编译报错

 

报错的原因:String类 与 Person类之间,不存在 父子类 关系!!!

再思考一问题:那么是不是只要存在父子类关系,父类的引用就可以转换成任意的子类类型后也能执行成功呢?
public class Test {
    
  public static void main(String[] args) {
     
     Person ps = new Student("Bob",24,1002);   // ps指向的是Student类型的对象
     ps.show();
     Student ts=(Student)ps;    // 编译成功
     int res = ts.getId();
     System.out.println(res);  // 且运行成功
     
     System.out.println("-------------");
     
     Teacher t=(Teacher)ps;   // 编译阶段 成功
 
     
  }
}
控制台结果:
Student类中重写后的show()方法
1002
-------------
Exception in thread "main" java.lang.ClassCastException: com.monkey1024.Student cannot be cast to com.monkey1024.Teacher
    at com.monkey1024.Test.main(Test.java:15)

 

 

 从输出结果发现:产生了java.lang.ClassCastException,类型转换异常

Teacher t=(Teacher)ps; // 编译阶段 成功
这行代码能够成功通过编译阶段, 但是在运行阶段报错了。

编译通过-----因为Teacher类 与 Person类之间确实存在父子关系,
运行报错----- Person ps = new Student("Bob",24,1002); ps真正指向的是Student对象,并不是Teacher。其实进行的是Student类型 转 Teacher类型,肯定报错!
ps真正指向的是Student类型的对象,而非Teacher类型的对象。


所以,代码在运行阶段的错误风险更高。为了避免类型转换异常的发生,添加判断:判断ps引用真正 指向 的对象是否为 Teacher类型即可!
public class Test {
    
 public static void main(String[] args) {
     
     Person ps = new Student("Bob",24,1002);
     ps.show();
     Student ts=(Student)ps;    // 编译成功
     int res = ts.getId();
     System.out.println(res);  // 且运行成功
     
     System.out.println("-------------");
     
     if(ps instanceof Teacher) {     // instanceof关键字 判断 ps真正的指向类型是不是Teacher类型
         Teacher t=(Teacher) ps;
System.out.println(
"ps可以放心地转换为Teacher类型"); }else { System.out.println("ps不能强制转换为Teacher类型"); } } }

 


运行结果:
Student类中重写后的show()方法
1002
-------------
ps不能强制转换为Teacher类型

 



instance关键字的作用


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

posted @ 2019-10-24 21:00  小茅棚  阅读(445)  评论(0编辑  收藏  举报