封装
封装的概念理解起来非常容易,我初次接触很容易地联想到了vue中的计算属性,或者python中的私有属性问题。他们在这个问题上显得极为相像。话不多说,上代码:
// 定义一个Person类class Person{
public String name;
public int age;
}
Person p1 = new Person();
p1.name = "zhangsan";
p1.age = 20;
先定义一个Person类再实例化一个p1对象,给p1的两个属性赋值。这看起来似乎并没有什么问题,但是这样Person类的属性可以随意地被外部自定义修改,并不是一件好事,于是我们把它改成私有。
// 将属性改成私有class Person{
private String name;
private int age;
}
将属性改成私有后就并不能被外部访问且随意地修改。但是问题来了,既不能被访问又不能被修改那定义的属性意义何在?所有必须得想个办法让它可以被访问且不能随便地被外部修改。
class Person{
private String name;
private int age;
// 获取name public String getName{
return name;
}
// 设置name public void setName(String name){
name = name;
}
// 获取age public int getAge{
return age;
}
// 设置age public void setAge(int age){
if (age > 0 && age < 100){
age = age;
}
}
}
Person p1 = new Person();
p1.setName("zhangsan");
p1.getName(); // "zhangsan"p1.setAge(20);
p1.getAge(); // 20
如此一来便达到了我们的目的,可以在设置方法里加一些判断条件既能让属性可以访问又不能被外部随意地修改,
这就是所谓的封装了。
继承
继承算是面向对象编程中尤为显著的特征了,说到面向对象,就少不了继承。何为继承?
老师是人,学生也是人,老师和学生都拥有人的特性,那么就可以说老师和学生都继承了人。
狗是动物,猫是动物,狗和猫都拥有动物的特性,那么就可以说狗和猫都继承了动物。
这种关系,就称为继承。继承的作用是把一些子类(老师,学生)共同的特性单独抽象成一个父类(人)。
// Person类class Person{
//...}
// Teacher类,继承Person类class Teacher extends Person{
// ...}
// Student类,继承Person类class Student extends Person{
// ...}
子类在定义时通过extends继承父类,获得父类所有的属性以及方法(私有的除外),大大简化了代码。
构造器
java中实例化一个对象会调用对应类中的构造方法,如果没有显示地指明构造方法,则默认为与类同名的方法。
class Person {
// 默认的构造方法 public Person(){
}
}
Person p1 = new Person();
//=====================分割线=====================class Person {
// 指定的构造方法 public Person(String name, int age){
this.name = name;
this.age = age;
}
}
Person p1 = new Person("zhangsan", 20);
如果指定了构造方法就必须在实例化时传入指定的参数列表,再像之前一样实例化则会报错。
也可以对构造方法进行重载,这样就可以多种形式去实例化了,如下:
class Person{
private String name;
private int age;
public Person(){
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
Person p1 = new Person(); // 两种方式都可以了Perosn p2 = new Person("zhangsan", 20);
注意,如果继承了父类,子类在实例化时默认会首先调用父类的构造方法。也就是说如果自定义了父类的构造方法则必须也在子类的构造方法中实现。这么说有些不太容易理解,上代码:
class Person{
protected String name;
protected int age;
// 自定义Person类的构造方法 public Person(String name, int age){
this.name = name;
this.age = age;
}
}
// Teacher类继承Person类class Teacher extends Person{
// ... protected int id;
}
Teacher t1 = new Teacher(); // 会报错,因为子类默认先调用父类的构造方法,要求传入参数// 修改后class Teacher extends Person{
public Teacher(String name, int age, int id){
super(name, age);
this.id = id;
}
}
Teacher t1 = new Teacher("zhangsan", 20);
子类中的super指向父类,以上将参数name和age传入super相当于传入并调用父类的构造方法,再对自身进行构造(this指向实例化对象自身这一点阐述了,参考JavaScript中的this);
方法重写
子类如果想扩展从父类继承来的方法或者做一些修改,可以对继承的父类中的方法进行重写,使用@override修饰。
class Person{
protected String name;
public void say() {
System.out.print("I am " + this.name);
}
}
// 子类class Teacher extends Person {
@override
public void say() {
System.out.print("Hello! Everybody!");
}
}
Teacher t1 = new Teacher();
t1.say(); // Hello! Everybody!
多态
什么叫多态?引用廖雪峰老师的一句话,多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。
光靠这一句话理解可能十分费劲,所以话不多话,上代码:
class Person{
public void run(){
System.out.print("I am person, i am running.")
}
}
class Teacher extends Person{
@override
public void run(){
System.out.print("I am teacher, i am running.")
}
}
Person p1 = new Person();
p1.run(); // I am person, i am running.Teacher t1 = new Teacher();
t1.run();// I am teacher, i am running.
看到以上代码你可能会说这结果不是显而易见的嘛,多态在哪呢。接着看:
class Person{
public void run(){
System.out.print("I am person, i am running.")
}
}
class Teacher extends Person{
@override
public void run(){
System.out.print("I am teacher, i am running.")
}
}
Person p2 = new Teacher();
看到这你可能就懵了,p2的类型指定为Person怎么能用Teacher实例化呢?
在java中这的确是可以的,因为Teacher是Person的子类,继承关系为:
Object => Person => Teacher
用Teacher类实例化却指定类型为Person,感觉是类向上转化了,这称为向上转型。子类的功能肯定是大于等于父类的,所以可以向上转化。那如果向下转型呢?答案是也可以,但是因为父类的方法是少于子类的,所以向下强制转化很可能失败。
class Person{
public void run(){
System.out.print("I am person, i am running.")
}
}
class Teacher extends Person{
@override
public void run(){
System.out.print("I am teacher, i am running.")
}
}
Person p1 = new Person();
Teacher t1 = (Teacher)p1; // 编译时会报错
一般情况下会有比较复杂的继承树,怎么判断究竟能不能转型呢?
instanceof
这是instanceof就派上用场了。instanceof用来判断一个类是否为某种类型。返回值为布尔值,为true则可以转型,为false则不能。
Person p = new Student();
if (p instanceof Student) {
// 判断成功才会向下转型: Student s = (Student) p; // 一定成功}
本篇多有参考廖雪峰老师的教程,详情可见廖雪峰的官方网站