Java学习——Java面向对象03(面向对象高级 上)
文章目录
一、继承
(1). 继承
先举例:
//最基本的Person类
public class Person {
public String name;
public int age;
//构造方法
public Person() {
}
//属性的获得与赋值方法
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 void show() {
System.out.println("这是个人,姓名:" + name + ",年龄为:" + age);
}
}
其次我们要定义一个学生类,学生满足Person类的属性及方法,所有可以使用继承方法:
//从Person类继承而来的Student类
public class Student extends Person{
private String school;
public Student(){
super();
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
//父类方法重写
@Overridde
public void show() {
System.out.println("这是个学生,姓名:" + name + ",年龄为:" + age + ", 学校为:" + school);
}
}
用一个Test类测试上面的情况:
//Test类
public class Test{
public static void main(String[] args) {
Student s = new Student();
s.setName("张三");
s.setAge(20);
s.setSchool("小小小学");
s.show();
}
}
运行结果:
分析:
我们使用的Student类 继承了 Person类,从而得以使用Person类里的属性及方法。在使用子类构建的对象时,会先加载父类。
继承:
Student类继承了Person类中的所有成员变量和方法,从上述代码可以见继承使用的关键字是 extends,extends后面的Person是父类。
如果在类的声明中没有使用extends关键字指明其父类,则默认父类为Object类。
所以说java.lang包中的Object类是 Java的根类,所有Java类包括数组都直接或间接继承了Object类,在Object类中定义了一些有关面向对象机制的基本方法,如equals()、toString()等方法。
例如我们定义一个类:
public class Person{ }
其实它被使用时,是这样的:
public class Person extends Object{ }
两者是一样的,不过是默认没有显示后面的继承。
子类继承父类是有访问权限限制的,限制来自权限修饰符:
也就是说当我们将Person类的属性,比如 name 或 age 前面的修饰符从public改为private时,子类Student便不能使用了。
(2). 调用父类的构造方法
构造方法是加载类时,自动调用的。
当子类实例化时,不仅需要初始化子类成员变量, 也需要初始化父类成员变量,初始化父类成员变量需要调用父类构造方法,子类使用 super 关键字调用父类构造方法。
例如,我么将上面的Person类中的构造方法,构建一个多参构造方法:
//Person类
//构造方法
public Person(String name,int age) {
System.out.println("Person类被创建!");
this.name = name;
this.age = age;
}
在Student类中构建多参构造方法:
//Student类
public Student(String name,int age,String school){
super(name,age);
System.out.println("Student类被创建!");
this.school = school;
}
运行结果:
有两个注意点:
输出按照顺序的不同类被创建的语句,可以看到,在使用子类对象构造方法时,先创建Student的父类。
我们若是在子类Student中的构造方法中将调用父类Person的构造方法 Super() 方法向下挪的时候发现报错。
结果:super调用父类构造方法必须位于子类构造方法的第一行,并且调用this不能和super()同时调用,不合乎逻辑。
(3). 方法的重写
如果子类方法完全与父类方法相同,即:相同的方法名、相同的参数列表和相同的返回值,只是方法体不同,这称为子类覆盖或重写(Override)父类方法。
我们在子类Student中,以注解@Override开头,重写了父类Person的show() 方法。在声明方法时添加@Override注解,@Override注解不是方法覆盖必须的,它只是锦上添花,但添加@Override注解有两个好处:
- 提高程序的可读性。
- 编译器检查@Override注解的方法在父类中是否 存在,如果不存在则报错。
注意方法重写时应遵循的规则:
- 参数列表必须完全与父类被重写的方法相同。
- 返回类型必须完全和被重写的方法的返回类型相同
- 重写后的方法不能比原方法有更严格的访问控制(可以相同)。例如将子类重写的方法show() 代码访问控制public修改private,那么会发生编译错误,但是可以声明。
- 覆盖后的方法不能比原方法产生更多的异常。
- 父类的成员方法只能被它的子类重写
方法重写(Override)与方法重载(Overload)的区别:
- 发生的位置:
重载:一个类中
重写:子父类中 - 参数列表限制:
重载:必须不同
重写:必须相同 - 返回值类型:
重载:与返回类型无关
重写:返回类型必须一致 - 访问权限:
重载:与访问权限无关
重写:子类的方法访问权限必须不能小于父类的方法访问权限 - 异常处理:
重载:与异常无关
重写:异常范围可以更小,但是不能抛出新的异常
二、多态
多态概念: 就是对象的多种表现形式,(多种体现形态)。
发生多态要有三个前提条件:
- 继承。多态发生一定要子类和父类之间。
- 覆盖。子类覆盖了父类的方法。
- 声明的变量类型是父类类型,但实例则指向子 类实例。
方法的 重载 和 重写 也是多态的一种,不过是方法的多态(相同方法名的多种形态)。
重载: 一个类中方法的多态性体现
重写: 子父类中方法的多态性体现
(1). 多态的使用
类似于基本数据类型的转换:
· 向上转型:将子类实例变为父类实例
格式:父类 父类对象 = 子类实例 ;
· 向下转型:将父类实例变为子类实例
格式:子类 子类对象 = (子类)父类实例 ;
其中类型转换可以用关键字 instanceof ,判断某个对象是否是指定类的实例。
格式:实例化对象 instanceof 类 //此操作返回boolean类型的数据
例:将 一、继承 中其余代码不变,Test.java代码调整为:
//Test.java
public class Test {
public static void main(String[] args) {
Student s = new Student("张三",20,"小小小学");
Person p = new Person();
say(p);
say(s);
}
public static void say(Person p) {
if(p instanceof Student) {
Student s = (Student)p;
s.show();
}else System.out.println("需要传入Student类型!!");
}
}
运行结果:
用以验证 关键字 instanceof 功能。
三、再谈 final 关键字
为了声明常量使用过final关 键字,在Java中final关键字的作用还有很多,final 关键字能修饰变量、方法和类。
(1). final修饰变量
final修饰的变量即成为常量,只能赋值一次,但是 final所修饰局部变量和成员变量有所不同。
- final修饰的局部变量必须使用之前被赋值一次才能使用。
- final修饰的成员变量在声明时没有赋值的叫“空白final变量”。空白final变量必须在构造方法或静态代码块中初始化
例如:
class FinalDemo {
void doSomething() {
// 没有在声明的同时赋值
final int e;
// 只能赋值一次
e = 100;
System.out.print(e);
// 声明的同时赋值
final int f = 200;
}
//实例常量
final int a = 5; // 直接赋值
final int b; // 空白final变量
//静态常量
final static int c = 12; // 直接赋值
final static int d; // 空白final变量
// 静态代码块
static {
// 初始化静态变量
d = 32;
}
// 构造方法
FinalDemo() {
// 初始化实例变量
b = 3;
// 第二次赋值,会发生编译错误
// b = 4;
}
}
(2). final修饰类
final修饰的类不能被继承。有时出于设计安全的目的,不想让自己编写的类被别人继承,这时可以使 用final关键字修饰父类。
(3). final修饰方法
final修饰的方法不能被子类覆盖。有时也是出于设计安全的目的,父类中的方法不想被别人覆盖,这时可以使用final关键字修饰父类中方法。