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关键字的作用