面向对象不仅仅在java中有体现,在C++中也有。Java面向对象三大特征
- 封装和隐藏
- 继承
- 多态
1.封装和隐藏
Java中通过将数据声明为private私有,再提供公共(public)的方法,getXXX和setXXX给外部使用,实现对类属性的操作,以期达到以下目的:
- 隐藏一个类中不需要对外部提供的实现细节
- 调用者只能通过公共方法来实现对属性的访问和操作,以限制对属性的非法或者不合理的操作
- 便于修改和维护,增加其可维护性
1.1属性函数设置
对于设置setXXX和getXXX在eclipse IDE中,有较为方便的方法,如下:
public class Person {
int age;
String name;
int sex;
}
选中数据属性,右键/source/Generate Getters and Setters ,勾选需要生成对应方法的属性
public class Person {
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
int age;
String name;
int sex;
}
即可生成对应的属性方法。
1.2.this关键字
自动生成的属性方法中有一个this关键字,Java中this的作用和其含义有点像,具体使用如下:
- 在方法内部使用时,即表示这个方法所属对象的引用
- 在构造器中使用时,表示该构造器正则初始化的对象
- this便是当前对象,即就是表示调用当前方法,构造方法,属性的对象,如上例中,如果有
Person p = new Person();
p.setAge(10);
this.age = age;等价于p.age = age;
- 如果是调用构造函数,则必须放置在第一行
如类Person开始没有写构造函数,此时会系统会提供一个无参的默认构造函数,但是如果添加一个带有参数的构造函数,则系统不会再调用默认的构造函数,要想使用必须手动添加一个无参的构造函数。eclipse中提供了快捷的方法,按下Alt+/选择就会生成构造函数。
public Person(int age,String name,int sex) {
this();//必须放置在首行
this.age = age;
this.name = name;
this.sex = sex;
// TODO Auto-generated constructor stub
}
public Person() {
// TODO Auto-generated constructor stub
System.out.println("调用了默认的构造函数");
}
因此,需要注意以下内容:
- Java每个类至少有一个构造函数
- 默认构造函数的修饰符与所属类的修饰符一致
- 一旦显示定义了构造函数,则系统不会提供默认的构造函数
- 一个类可以创建多个重载的构造函数
- 父类的构造函数不能被子类继承
2.继承
定义一个父类为Person,描述信息又姓名,年龄等信息
现在有一个学生类,也有姓名和年龄,且存在一个子类是is-a父类的关系
当然还有一个工人类,教师类,程序员类等
extends关键字.
注意:子类不是父类的子集,而是父类的扩展,简单的理解就是“青出于蓝而胜于蓝”,且Java中一个子类只能派生于一个父类,就像一个汉字只有一个父亲一样。
2.1.方法的重新override
在子类中可以根据需要对从父类中继承来的方法进行改造,也称之为方法的重置、覆盖。在程序执行时,子类的方法会覆盖父类的方法。
- 重新方法必须和被重写的方法有相同的方法名称、参数类别和返回类型
- 重新方法不能使用比被重写方法更严格的访问权限
- 重新和被重写的方法必须同时为static或者非static
- 子类抛出的异常不能大于父类被重写方法的异常
当然eclipse也提供了重写方法的快速操作方法
@Override
public int getAge() {
// TODO Auto-generated method stub
return super.getAge();
}
选中之后会自动生成重写(override)方法的模板,既然这里使用到了super,下面介绍一下super关键字
2.2. super
在Java中使用super来调用父类中的指定操作:
- super用于访问父类定义的属性
- super用于调用父类中的成员方法
- super可用于在子类的构造方法中调用父类的构造方法,注意必须放置在第一行
注意:如果子类中出现了和父类的同名函数,则可使用super进行区分,且super的追溯不限于父类,可以是租父类
super和this:this代表类对象的引用,super代表父类的内存空间的引用。
既然说到了调用父类构造函数的问题,这里需要有几点必须注意:
- 子类中所有的构造函数默认都会访问父类中空参数的构造函数
- 当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super指定调用本类或者父类中相应的构造函数,且必须放置在第一行
- 如果子类构造函数没有显式调用父类或者本类的构造函数,且父类中又没有无参的构造函数时,此时编译出错
public class Person {
public Person(int age,String name,int sex) {
this();
this.age = age;
this.name = name;
this.sex = sex;
// TODO Auto-generated constructor stub
}
public Person() {
// TODO Auto-generated constructor stub
System.out.println("调用了默认的构造函数");
}
}
public class Student extends Person {
String school;
public Student(int age,String name,int sex,String sch) {
// TODO Auto-generated constructor stub
super(age,name,sex);
this.school = sch;
}
}
2.3.简单类对象的实例化过程
Person p = new Person();
2.4.子类对象的实例化过程
Student stu = new Student();
3.多态
多态性是面向对象的中最重要的概念,在Java中有两种体现
- 方法的重载:overload重载,同名方法,参数列表不同;override重写,方法体不同
- 对象的多态性,可以直接应用在抽象类和接口上
Java的引用变量有两种类型:编译时类型和运行时类型,编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。若编译时类型和运行时类型不同,则出现多态。
如何区分呢?在Student stu = new Student();中,Student stu就认为是编译时类型,后面的new Student()理解为运行时类型。所所以上面的出现 “多态”就是对象的多态。
Student stu = new Student();
Person p = new Person();
//父类的引用对象指向子类的实例
Person e = new Student();
那如果是下面的场景呢?当前引用对象p引用的是哪个实例?
p = new Student();
当然是指向Student的实例
因为是子类可以看成是特殊的父类,所以父类对象的引用可以指向子类的对象,这就是向上转型(子类的对象可以给父类类型的变量引用)
注意:一个引用类型的变量如果声明为父类类型,但实际引用的是子类对象,那么该变量就不能访问子类中添加的属性和和方法
Person p = new Person();
//父类的引用对象指向子类的实例
Person e = new Student();
e.school = "school";//非法
因为属性是在编译时确定的,编译时e为Person类,没有school属性,所以编译会报错
那方法调用呢?
public class Person {
int age;
String name;
int sex;
public void showInfor() {
System.out.println("调用Person中的shwoInfo");
}
}
public class Student extends Person {
String school;
@Override
public void showInfor() {
// TODO Auto-generated method stub
// super.showInfor();
System.out.println("调用Student中的shwoInfo");
}
public static void main(String[] args) {
Student stu = new Student();
stu.showInfor();
Person p = new Person();
p.showInfor();
//父类的引用对象指向子类的实例
Person e = new Student();
e.showInfor();//此时e.showInfor会调用哪个类的方法呢?
}
}
输出结果结果为:
调用Student中的shwoInfo
调用Person中的shwoInfo
调用Student中的shwoInfo
这个就是虚拟方法调用
方法调用是在运行的时候确定的,所以调用的是Student类的方法,也就是动态绑定