工欲善其事必先利其器,磨刀不误砍柴工,要把技术学好,必须把基础抓劳,这段时间对java面向对象的基础重新学习一遍,这里做个总结。
一、面向对象程序设计中的重要概念
面向对象的程序设计过程中有两个重要的概念:类(class)和对象(object,也被称为实例),类是一批对象的抽象,可以把类理解为某种概念,对象才是一个具体存在的实体,比如日常中我们所说的人就是实例,而不是人类。
二、面向对象的三大特征
Java是面向对象的程序设计语言,支持面向对象的三大特征:封装,继承,多态,java提供了private、protect、public等访问控制修饰符来实现良好的封装,提供了extends关键字来让子类继承父类,继承是代码复用 的重要手段,使用继承关系来实现复用时,子类对象可以直接复值给父类变量,这个变量就具有多态性。
1.封装
封装:指的是将对象的状态信息(属性)隐藏在对象的内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法(一般是set和get方法)来实现对内部信息的操作和访问。对一个类或对象实现良好的封装可以实现以下目的:
隐藏类的实现细节
让使用者只能通过实现预定的方法来访问数据限制对field的不合理访问
可以进行数据检查,从而保证对象信息的完整性
便于修改,提高代码的可维护性
使用访问控制符:
Private(当前类访问权限):如果一个类里的成员使用private访问控制符修饰,则这个成员只能在当前类的内部被访问。一般我们在对类进行封装时经常使用。
Default (包访问权限):如果一个类里的成员或者一个外部类不适用任何访问控制修饰符,我们称它是包访问修饰符,default访问控制的成员或者外部类可以被相同包名下的其他类访问。
Protect(子类访问权限):如果一个成员使用protect访问控制修饰权限,那么这个成员可以被同一个包的其他类访问,也可以被不同包的子类访问,同常情况下,如果使用protect修饰一个方法,通常是希望其子类重写这个方法。
Public(公共访问权限):如果一个成员或者一个外部类使用public访问控制修饰符,那么这个成员或外部类就可以被所有类访问,不管访问类和被访问类是否处于同一包名,是否具有父子继承关系。
例如对people对象的封装:
package com.wind.test;
public class People
{
private int age;
private char sex;
private String name;
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public char getSex()
{
return sex;
}
public void setSex(char sex)
{
this.sex = sex;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
2.继承
Java的继承通过extends关键字来实现,实现继承的类被称为子类,被继承的类称为父类。
父类和子类的关系是一种特殊与一般的关系,例如水果和苹果的关系,苹果继承了水果,苹果是水果的子类,苹果是一种特殊的水果。
例如:
public class Apple extends Fruit
{
}
重写父类的方法:
子类继承了父类,子类是一个特殊的父类,大部分时候,子类以父类为基础,增加新的field和方法,但是有时候父类的方法不一定时候子类的业务,则此时子类需要重写父类的方法,比如鸵鸟也是一种特殊的鸟类但是父类(鸟)的飞行方法(fly)并不适用于鸵鸟,因此鸵鸟需要重写父类的飞行方法,代码如下:
package com.wind.test;
public class Ostrich extends Bird
{
public void fly()
{
System.out.println("我不会飞!!");
}
}
package com.wind.test;
public class Bird
{
public void fly()
{
System.out.println("我会飞,我骄傲");
}
}
方法重写要遵循两同两小一大规则,两同为方法名相同,形参相同;两小指子类返回值类型应比父类的返回值类型更小或者是相等,子类方法声明抛出的异常应比父类方法声明抛出的异常类更小或是相等;一大指的是子类的方法访问权限应比父类方法的访问权限更大或相等。
调用父类的构造器
在父类构造器中调用另一个重载的构造器使用this调用来完成,在子类构造器调用父类构造器使用super调用来完成。
当调用子类构造器来初始化子对象时,父类构造器总会在子类构造器之前执行;不仅如此,执行父类构造器时,系统会再次上溯执行父类构造器,依次类推,创建任何Java对象,最先执行的总是java.lang.Object类的构造器。
例子如下:
public class Creature
{
public Creature()
{
System.out.println("Creature的无参构造器");
}
}
class Animal extends Creature
{
public Animal(String name)
{
System.out.println("animal 带一个参数的构造器," + "动物的名字是"+name);
}
public Animal(String name, int age)
{
this(name);//调用同一个重载的构造器
System.out.println("Animal 带两个参数的构造器"+ "动物的年龄为:"+age);
}
}
public class Wolf extends Animal
{
public Wolf()
{
super("狼",3);//使用super显示调用父类的带两个参数的构造器
System.out.println("Wolf的无参构造器");
}
public static void main(String[] args)
{
new Wolf();
}
}
运行结果:
Creature的无参构造器
animal 带一个参数的构造器,动物的名字是狼
Animal 带两个参数的构造器动物的年龄为:3
Wolf的无参构造器
3.多态:
Java引用变量有两个类型:一个编译时类型,一个运行时类型。编译时类型有声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现所谓的多态。
例子如下:
public class BaseClass
{
private int age = 6;
public void publish()
{
System.out.println("父类的普通方法publish");
}
public void test()
{
System.out.println("父类被覆盖的方法test");
}
}
public class SubClass extends BaseClass
{
public String name = "6";
public void test()
{
System.out.println("子类的覆盖父类的方法test");
}
public void sub()
{
System.out.println("子类的普通方法sub");
}
public static void main(String[] args)
{
BaseClass bc = new BaseClass();//编译和运行时类型完全相同不存在多态
bc.publish();
bc.test();
//=====================================================//
SubClass sb = new SubClass();//编译和运行时类型完全相同不存在多态
sb.publish();
sb.test();
//=====================================================//
BaseClass base = new SubClass();//编译和运行时类型不相同存在多态
base.publish();
base.test();
}
}
执行结果:
父类的普通方法publish
父类被覆盖的方法test
父类的普通方法publish
子类的覆盖父类的方法test
父类的普通方法publish
子类的覆盖父类的方法test
对于上述3个引用变量,bc和sb他们的编译类型和运行时类型完全一致,因此调用他们的field和方法非常正常,完全没有问题,但是第三个变量base比较特殊,编译时的类型是BaseClass,而运行时类型是SubClass,当调用该引用变量的test方法时实际执行的是Subclass方法的test方法,这就出现了多态。
当一个子类对象直接赋给父类引用变量时,例如上面的BaseClass base = new SubClass();,这个base引用变量的编译时类型是BaseClass类型二运行时类型是SubClass,当运行时调用该引用变量的方法时,其方法的行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就可能出现:相同的变量,调用同一个方法时呈现出多种不同的行为特征,这就是多态。