特性一:封装
封装(也叫信息隐藏或者数据访问保护!)
定义:类通过暴露有限的访问接口、授权外部仅能通过类提供的方式(或者函数)来访问内部信息或者数据!
实现:对于语言来说一般都会提供相对应的语法机制来支持!就是我们的访问修饰符!
解决什么问题勒?
1.保护数据不被随意修改,提供代码可维护性;
说明:如果对类中属性的访问不做限制,那任何代码都可以访问、修改类中的属性,看似更加的灵活,但不过过度的灵活会导致修改的逻辑可以散落在代码中的各个角落,就会影响代码的可读性、可维护性。特别是对于对改业务逻辑不是很理解的人。假设在一个银行卡系统中银行卡的实体类,那么该实体类属性:银行卡号,应该只能对外提供get方法而不能提供set方法,不是很懂业务逻辑的人不小心在哪里修改了一下属性值那么就会产生大的问题;
2.仅暴露有限的必要的接口,提高类的易用性!
比如一个遥控器,如果提供了太多太多按钮的化,那么是不是第一次用的时候就会非常麻烦?类也是如此如果没有方法的私有化的话,那么当你去查看这个类的方法时很有可能需要较为完整的查看这个类的代码!
代码实例:
public class Person {
private int id;
private String name;
private Person person;
public int getId() {
return id;
}
public String getName() {
return name;
}
public Person getPerson() {
return person;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPerson(Person person) {
this.person = person;
}
}
特性二:继承
为描述和处理 个人信息,定义类Person:
class Person {
public String name;
public int age;
public Date birthDate;
public String getInfo(){
// ...
}
}
为描述和处理 学生信息,定义类Student:
class Student {
public String name;
public int age;
public Date birthDate;
public String school;
public String getInfo() {
// ...
}
}
通过继承,简化Student类的定义:
class Person {
public String name;
public int age;
public Date birthDate;
public String getInfo() {
// ...
}
}
class Student extends Person {
public String school;
}
Student类继承了父类Person的所有属性和方法,并增加了一个属性school。
Person中的属性和方法,Student都可以使用。
- 为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,
那么多个类无需再定义这些属性和行为,只要继承那个类即可。 - 此处的多个类称为 子类( 派生类),单独的这个类称为父类(基类
或超类)。可以理解为:“子类 is a 父类” - 类继承语法规则:
class Subclass extends SuperClass{ }
作用:
- 继承的出现减少了代码冗余,提高了代码的复用性。
- 继承的出现,更有利于功能的扩展。
- 继承的出现让类与类之间产生了关系,提供了多态的前提。
注意:不要仅为了获取其他类中某个功能而去继承
- 子类继承了父类,就继承了父类的方法和属性。
- 在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
- 在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展” 。
关于继承的规则:
子类不能直接访问父类中私有的(private)的成员变量和方法。
Java只支持单继承和多层继承,不允许多重继承 - 一个子类只能有一个父类
- 一个父类可以派生出多个子类
class SubDemo extends Demo{ } //ok
class SubDemo extends Demo1,Demo2…//error
方法的重载与重写(override/overwrite)
定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
子类不能重写父类中声明为private权限的方法 - 子类方法抛出的异常不能大于父类被重写方法的异常
注意:
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
重写方法举例(1):
public class Person {
public String name;
public int age;
public String getInfo() {
return "Name: "+ name + "\n" +"age: "+ age;
}
}
public class Student extends Person {
public String school;
public String getInfo() { //重写方法
return "Name: "+ name + "\nage: "+ age
+ "\nschool: "+ school;
}
public static void main(String args[]){
Student s1=new Student();
s1.name="Bob";
s1.age=20;
s1.school="school2";
System.out.println(s1.getInfo()); //Name:Bob age:20 school:school2
}
}
Person p1=new Person();
//调用Person类的getInfo()方法
p1.getInfo();
Student s1=new Student();
//调用Student类的getInfo()方法
s1.getInfo();
这是一种“多态性”:同名的方法,用不同的对
象来区分调用的是哪一个方法。
重写方法举例(2):
class Parent {
public void method1() {}
}
class Child extends Parent {
//非法,子类中的method1()的访问权限private比被覆盖方法的访问权限public小
private void method1() {}
}
public class UseBoth {
public static void main(String[] args) {
Parent p1 = new Parent();
Child c1 = new Child();
p1.method1();
c1.method1();
}
}
关键字:super
- 在Java类中使用super来调用父类中的指定操作:
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造器中调用父类的构造器 - 注意:
尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
super的追溯不仅限于直接父类
super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识。
关键字super 举例:
class Person {
protected String name = "张三";
protected int age;
public String getInfo() {
return "Name: " + name + "\nage: " + age;
}
}
class Student extends Person {
protected String name = "李四";
private String school = "New Oriental";
public String getSchool() {
return school;
}
public String getInfo() {
return super.getInfo() + "\nschool: " + school;
}}
public class StudentTest {
public static void main(String[] args) {
Student st = new Student();
System.out.println(st.getInfo());
}}
- 调用父类的构造
子类中所有的构造器 默认都会访问父类中 空参数的构造器
当父类中没有空参数的构造器时,子类的构造器必须通过this(参 参表 数列表)或者super( 参数列表)语句指定调用本类或者父类中相应的构造器。同时,只能”二选一”,且必须放在构造器的首行
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则 编译出错
调用父类构造 器:
public class Person {
private String name;
private int age;
private Date birthDate;
public Person(String name, int age, Date d) {
this.name = name;
this.age = age;
this.birthDate = d;
}
public Person(String name, int age) {
this(name, age, null);
}
public Person(String name, Date d) {
this(name, 30, d);
}
public Person(String name) {
this(name, 30);
}
}
this 和super 的区别
特征三:多态性
-
多态性,是面向对象中最重要的概念,在Java中的体现:
对象 的 多态性:父类的引用指向子类的对象
可以直接应用在抽象类和接口上 -
Java引用变量有两个类型: 编译时类型和 运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。
若编译时类型和运行时类型不一致 , 就出现了对象的多态性 (Polymorphism)
多态情况下 , “ 看左边 ” : 看的是父类的引用(父类中不具备子类特有的方法)
“ 看右边 ” : 看的是子类的对象(实际运行的是子类重写父类的方法) -
对象的多态 —在Java中,子类的对象可以替代父类的对象使用
一个变量只能有一种确定的数据类型
一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象
子类可看做是特殊的父类 , 所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就 不能 再访问子类中添加的属性和方法
Student m = new Student();
m.school = “pku”; // 合法,Student 类有school 成员变量
Person e = new Student();
e.school = “pku”; // 非法,Person 类没有school
属性是在编译时确定的,编译时e为Person 类型,没有school 成员变量,因而编译错误。
多态性应用举例:
public class Test {
public void method(Person e) {
// ……
e.getInfo();
}
public static void main(Stirng args[]) {
Test t = new Test();
Student m = new Student();
t.method(m); // 子类的对象m传送给父类类型的参数e
}
}
虚拟方法调用(Virtual Method Invocation)
- 正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
- 虚拟方法调用( 多态情况下 下)
子 类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
Person e = new Student();
e.getInfo(); // 调用Student 类的getInfo() 方法
- 编译时类型和运行时类型
编译时e 为Person 类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo() 方法。—— 动态绑定
小结:方法的重载与重写
从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了
不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,
这称为“早绑定”或“静态绑定”;
而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
多态小结
- 多态作用:
提高了代码的通用性,常称作接口重用 - 前提 :
需要存在继承或者实现关系
有方法的重写 - 成员方法:
编译时:要查看引用变量所声明的类中是否有所调用的方法。
运行时:调用实际new的对象所属的类中的重写方法。 - 成员 变量:
不具备多态性,只看引用变量所声明的类。