前言
本文主要梳理面向对象程序特性三大特性:封装,继承,多态。这里面的知识有点杂,又相互交错,因此做出梳理方便复习!
一、封装
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。Java中主要通过类和访问权限来实现封装,在这里我们首先介绍一下访问限定符。
范围 | private | default | protected | public |
同一个包中的同一个类 | √ | √ | √ | √ |
同一个包中的不同类 | √ | √ | √ | |
不同包中的子类 | √ | √ | ||
不同类中的非子类 | √ |
public:修饰某一类中的成员或者成员方法时,可以在其他类的其他包中被调用。
protected:修饰某一类中的成员或者成员方法时,可以被自己包中的不同类中被调用,同时可以在其他包当中的子类调用该成员或者成员方法,(注意:该子类继承了父类是protected修饰的成员所在的类)
default:默认类,当没有访问限定符时,就默认是default类。
private:只能在同一个包中的同一个类里面调用。
二、继承
1.继承的理解
继承,以一个通俗的比喻来说就是一只哈士奇拥有狗类的一些特性,同时哈士奇又可以有自己的特性,这里我们把哈士奇叫做子类,狗类叫做父类。
public class Dog{//父类Dog
public String name;
public int age;
public Dog(String name,int age){
this.name = name;
this.age = age;
}
public void printDog(){
System.out.println("name "+this.name+" age "+this.age);
}
public static void main(String[] args) {
HaShiQi myDog = new HaShiQi("小花狗",18);
myDog.printDog();
myDog.attack();
}
}
class HaShiQi extends Dog{//子类哈士奇
public HaShiQi(String name,int age){
super(name,age);
}
public void attack(){
System.out.println("哈士奇正在攻击");
}
}
上述代码,我们通过创建HaShiQi类型的对象myDog,通过myDog调用了子类HaShiQi中的attack方法,同时可以调用父类中的printDog方法。
由此可见,继承了Dog的HaShiQi,HaShiQi所创建的对象既可以调用自己(子类)特有的方法,也可以调用Dog(父类)中的方法,这是继承的特性之一。
2.super,final关键字
super
通过子类中含有自己的构造方法,同时在自己的构造方法里面一定要调用父类的构造方法
public HaShiQi(String name,int age){
super(name,age);
}
super是用来调用父类的构造方法的关键字。
1.如果父类是显式定义且无参或者是默认的构造方法,在子类构造方法第一行默认隐含super()的调用,即默认调用了父类构造方法。
2.super()语句必须在子类构造方法里的第一句。
3.super()只能在子类构造方法出现一次,并且不能和this同时出现。
super还可以用来访问父类的成员和成员方法
public class Dog{
public String name;
public int age;
public Dog(String name,int age){
this.name = name;
this.age = age;
}
public void printDog(){
System.out.println("name "+this.name+" age "+this.age);
}
public static void main(String[] args) {
HaShiQi myDog = new HaShiQi("小花狗",18);
myDog.printHaShiQi();
// myDog.printDog();
myDog.attack();
}
}
class HaShiQi extends Dog{
public String name;
public int age;
public HaShiQi(String name,int age){
super(name,age);
}
public void printHaShiQi(){
System.out.println("name "+super.name+" age "+super.age);
}
public void attack(){
System.out.println("哈士奇正在攻击");
}
}
以上代码的printHaShiQi中我们把this.name和this.age改为了super.name和super.age
这样一来,最后打印的是父类中的小花狗 和 18 而不是子类中默认初始化的null 和 0
final
1.修饰变量,表示常量,其无法被修改。
final int a = 10;
a = 20; //编译出错
当你尝试修改final修饰的变量时,编译器会报错
2.修饰“类”,被修饰的类无法被继承
final public class Dog{
…………
}
class HaShiQi extends Dog{
…………
}
当你尝试继承被final修饰的类时,编译器会报错
3.修饰方法,被修饰的方法无法被“重写”
3.父类和子类存在同名成员
下面代码对父类的name和age分别赋值 小花狗 18
public class Dog{
public String name;
public int age;
public Dog(String name,int age){
this.name = name;
this.age = age;
}
public void printDog(){
System.out.println("name "+this.name+" age "+this.age);
}
public static void main(String[] args) {
HaShiQi myDog = new HaShiQi("小花狗",18);
myDog.printHaShiQi();
myDog.printDog();
myDog.attack();
}
}
class HaShiQi extends Dog{
public String name;
public int age;
public HaShiQi(String name,int age){
super(name,age);
}
public void printHaShiQi(){
System.out.println("name "+this.name+" age "+this.age);
}
public void attack(){
System.out.println("哈士奇正在攻击");
}
}
上述代码中,子类和父类都定义了name和age,但是调用方法时,如果是父类的方法则会使用父类的name和age,如果是子类的方法则会使用子类的name和age
但是要是子类没有定义name和age,则会输出父类的name和age
public class Dog{
public String name;
public int age;
public Dog(String name,int age){
this.name = name;
this.age = age;
}
public void printDog(){
System.out.println("name "+this.name+" age "+this.age);
}
public static void main(String[] args) {
HaShiQi myDog = new HaShiQi("小花狗",18);
myDog.printHaShiQi();
myDog.printDog();
myDog.attack();
}
}
class HaShiQi extends Dog{
// public String name;
// public int age;
public HaShiQi(String name,int age){
super(name,age);
}
public void printHaShiQi(){
System.out.println("name "+this.name+" age "+this.age);
}
public void attack(){
System.out.println("哈士奇正在攻击");
}
}
从上面代码来看,如果调用函数要使用类中的成员。
如果是调用子类中的方法,则会优先使用子类中的成员,如果子类没有才会使用父类
如果是调用父类中的方法,会使用父类中的成员。
4.初始化
这里我们主要是研究静态代码块和实例代码块以及构造方法的初始化顺序
代码块:属于类中的成员,即类的一部分 类似于方法,将逻辑语句封装在方法提中,通过{ }包围起来。但和方法不同,没有方法名,没有返回值,没有参数,只有方法体,而且不用通过对象或类显示调用,而是在加载类的时候或者创建对象的时候隐式调用。
public class Dog2{
public String name;
public int age;
public Dog2(String name,int age){
this.name = name;
this.age = age;
System.out.println("构造方法");
}
{
System.out.println("实例代码块");
}
static{
System.out.println("静态代码块");
}
public void printDog(){
System.out.println("name "+this.name+" age "+this.age);
}
public static void main(String[] args) {
Dog2 myDog = new Dog2("小花狗",18);
// myDog.printHaShiQi2();
// myDog.printDog();
// myDog.attack();
}
}
上图中{}里面的是实例代码,static{}里面是静态代码
从控制台输出的结果来看,初始化顺序是先静态代码,再实例代码,再构造方法
上面是没有继承的情况下的初始化顺序,接下来我们看一下有继承情况下的初始化顺序
public class Dog2{
public String name;
public int age;
public Dog2(String name,int age){
this.name = name;
this.age = age;
System.out.println("父类的构造方法");
}
{
System.out.println("父类的实例代码块");
}
static{
System.out.println("父类的静态代码块");
}
public void printDog(){
System.out.println("name "+this.name+" age "+this.age);
}
public static void main(String[] args) {
HaShiQi2 myDog = new HaShiQi2("小花狗",18);
// myDog.printHaShiQi2();
// myDog.printDog();
// myDog.attack();
}
}
class HaShiQi2 extends Dog2{
public String name;
public int age;
public HaShiQi2(String name,int age){
super(name,age);
System.out.println("子类的构造方法");
}
{
System.out.println("子类的实例代码块");
}
static{
System.out.println("子类的静态代码块");
}
//public void printHaShiQi2(){
// System.out.println("name "+this.name+" age "+this.age);
//}
// public void attack(){
// System.out.println("哈士奇正在攻击");
//}
}
从结果来看,会优先初始化父类的静态代码块,子类的静态代码块,然后再初始化父类的实例代码块和构造方法,再初始化子类的实例代码块和构造方法。
5.继承方式
单继承 | public class Dog{ } class HaShiQi extends Dog{ } |
多层继承 | public class Dog{ } class HaShiQi extends Dog{ } class MyPet extends HaShiQi{ } |
多对一(多个子类继承一个父类) | public class Dog{ } class HaShiQi extends Dog{ } class MyPet extends Dog{ } |
但是不支持一个子类继承多个父类 | ------------------------------------------ |
三、多态
1.多态的理解
2.多态实现的三大条件
1.必须在继承的条件下
2.子类必须要对父类中的方法进行重写
3.通过父类的引用调用重写方法
继承在上面已经举例阐述了,现在让我们来看一看“重写”是什么
3.重写
重写是指在子类当中,写出与父类方法同名并且同参数列表的方法
比如下面哈士奇和金毛当中对父类printDog的重写
public class Dog{
public String name;
public int age;
public Dog(String name,int age){
this.name = name;
this.age = age;
}
public void printDog(){
System.out.println("name "+this.name+" age "+this.age);
}
public static void main(String[] args) {
Dog myDog = new HaShiQi("小哈狗",18);
Dog myDog2 = new JingMao("小金狗",72);
myDog.printDog();
myDog2.printDog();
}
}
class HaShiQi extends Dog{
public String name;
public int age;
public HaShiQi(String name,int age){
super("狗类",-1);
this.name = name;
this.age = age;
}
public void printDog(){
System.out.println("name "+this.name+" age "+this.age);
}
}
class JingMao extends Dog {
String name;
int age;
public JingMao(String name, int age) {
super("狗类", -1);
this.name = name;
this.age = age;
}
public void printDog() {
System.out.println("name " + this.name + " age " + this.age);
}
}
上面是方法的重写,将方法重写的细则归结起来就是:
1.重写的方法可以用@Override注释。这个注释可以帮助我们检查重写的方法是否有语法错误,比如把printDog写成了printfDog,这个时候编译器就会报错 |
2.子类中重写的方法必须与父类对应的方法同名,同参数列表 |
3.子类中重写的方法的访问权限必须大于等于父类对应方法中的权限 |
4.子类重写的方法的返回类型一般不能修改 |
4.向上转型
用父类类型创建一个引用,该引用指向一个子类对象。
Dog myDog = new HaShiQi("小哈狗",18);
Dog myDog2 = new JingMao("小金狗",72);
上述代码,
myDog指向一个HaShiQi的对象
myDog2指向一个JingMao的对象
5.向下转型
我们需要了解的是向上转型之后无法直接调用子类自己独特的方法(就是父类没有的方法)。
因此,向下转型应运而生,通过向下转型我们可以调用子类自己独特的方法。
public class Dog{
public String name;
public int age;
public Dog(String name,int age){
this.name = name;
this.age = age;
}
public void printDog(){
System.out.println("name "+this.name+" age "+this.age);
}
public static void main(String[] args) {
Dog myDog = new HaShiQi("小哈狗",18);
HaShiQi myDog21 = (HaShiQi)myDog;//将myDog强制转换为HaShiQi类型,然后赋值给myDog21
myDog21.attack();//myDog21是HaShiQi类型,因此可以调HaShiQi独有的方法attack
}
}
class HaShiQi extends Dog{
public String name;
public int age;
public HaShiQi(String name,int age){
super("狗类",-1);
this.name = name;
this.age = age;
}
public void attack() {
System.out.println(this.name + "正在攻击");
}
public void printDog(){
System.out.println("name "+this.name+" age "+this.age);
}
}
总结
本文简要的介绍了封装,继承,多态,可能主要是对知识的梳理,供复习使用!未完待续